Skip to content

feat: add edge-scrum Jira config-as-code with CI automation#14

Draft
jeff-roche wants to merge 1 commit intoopenshift-eng:mainfrom
jeff-roche:feat/edge-scrum-jira-config
Draft

feat: add edge-scrum Jira config-as-code with CI automation#14
jeff-roche wants to merge 1 commit intoopenshift-eng:mainfrom
jeff-roche:feat/edge-scrum-jira-config

Conversation

@jeff-roche
Copy link
Copy Markdown
Contributor

Summary

  • Adds edge-scrum/.jira-config/ as the source of truth for OCPEDGE Jira metadata (boards, filters, projects, components, labels)
  • Adds edge-scrum/.ci/scripts/ with sync scripts that apply config changes to Jira via REST API
  • Adds edge-scrum/.ci/schemas/ with JSON Schema definitions for all config files
  • Adds GitHub Actions workflows for PR validation and post-merge apply

Workflows

PR (jira-sync-pr.yml) — triggers on .jira-config/** changes:

  • validate-configs — validates JSON against schemas, checks for duplicate IDs/names
  • validate-readme — enforces that .jira-config/README.md is updated alongside config changes
  • dry-run — previews what would be synced to Jira without making API calls

Post-merge (jira-sync-apply.yml) — triggers on push to main or workflow_dispatch:

  • Runs validate-configs before applying (defense in depth)
  • Applies only the changed configs (diff-based); workflow_dispatch triggers a full sync
  • Concurrency control prevents simultaneous syncs

Security

  • Credentials sourced from environment variables only — no .env files committed
  • JIRA_URL defaults to https://redhat.atlassian.net; only JIRA_USERNAME and JIRA_API_TOKEN need to be set as repo secrets
  • Auth passed via Authorization: Basic header (token not exposed in process table)
  • Secure temp files via mktemp -d with trap cleanup

Test plan

  • Add JIRA_USERNAME and JIRA_API_TOKEN repo secrets in GitHub
  • Verify validate-configs job passes on this PR
  • Verify validate-readme job passes on this PR
  • Verify dry-run job passes and outputs expected changes
  • After merge, verify apply job runs and syncs configs to Jira

🤖 Generated with Claude Code

Introduces the edge-scrum tooling directory with:
- .jira-config/ — JSON source of truth for boards, filters, projects,
  components, and labels managed by the OCPEDGE team
- .ci/scripts/ — sync scripts that apply config changes to Jira via
  REST API; credentials sourced from environment variables only
- .ci/schemas/ — JSON Schema definitions for all config files
- GitHub Actions workflows for PR validation (schema check, README
  sync enforcement, dry-run preview) and post-merge apply with
  concurrency control and manual workflow_dispatch trigger

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@openshift-ci
Copy link
Copy Markdown

openshift-ci Bot commented Mar 25, 2026

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: jeff-roche

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@openshift-ci openshift-ci Bot added the approved Indicates a PR has been approved by an approver from all required OWNERS files. label Mar 25, 2026
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Mar 25, 2026

Walkthrough

This PR introduces Jira configuration-as-code infrastructure, adding GitHub Actions workflows for validation and synchronization, JSON schema definitions, Bash scripts for CI/CD automation and API integration, Jira metadata configuration files, and supporting documentation.

Changes

Cohort / File(s) Summary
GitHub Actions Workflows
.github/workflows/jira-sync-apply.yml, .github/workflows/jira-sync-pr.yml
Two workflows: jira-sync-apply.yml triggered on pushes to main for .jira-config/** changes, validates configs and applies changes to Jira via REST API with credentials; jira-sync-pr.yml triggered on pull requests, validates configs, ensures README sync, and performs dry-run previews.
JSON Schema Definitions
edge-scrum/.ci/schemas/*.schema.json
Five JSON Schema files (boards, filters, projects, components, labels) validating structure and constraints of Jira configuration metadata, including required fields, enums, regex patterns, and nested object validation.
Validation & Orchestration Scripts
edge-scrum/.ci/scripts/validate-configs.sh, edge-scrum/.ci/scripts/validate-readme-sync.sh, edge-scrum/.ci/scripts/apply-changes.sh
Scripts for validating JSON schemas and duplicate values, enforcing README updates alongside config changes, and orchestrating syncs of changed metadata files to Jira with support for dry-run mode and change detection.
Per-Entity Sync Scripts
edge-scrum/.ci/scripts/sync-boards.sh, edge-scrum/.ci/scripts/sync-filters.sh, edge-scrum/.ci/scripts/sync-projects.sh, edge-scrum/.ci/scripts/sync-components.sh
Four Bash scripts that read Jira metadata from config files and apply updates via Jira REST API (PUT requests), with dry-run support, error handling, and per-entity validation and logging.
Jira Configuration Files
edge-scrum/.jira-config/boards.json, edge-scrum/.jira-config/filters.json, edge-scrum/.jira-config/projects.json, edge-scrum/.jira-config/components.json, edge-scrum/.jira-config/labels.json
Five JSON configuration files defining Jira metadata (3 boards, 14 filters, 1 project, 8 components, 7 labels) for the OpenShift Edge project with schema-validated structure and API-syncable properties.
Documentation
edge-scrum/README.md, edge-scrum/.jira-config/README.md, edge-scrum/.ci/README.md
Three README files documenting the configuration-as-code workflow, metadata structure and current configuration, CI script functionality, API endpoints, and local execution instructions.
Configuration
.gitignore
Updated to exclude .env and **/.env files from version control.

Sequence Diagram

sequenceDiagram
    participant GitHub as GitHub
    participant Workflow as GitHub Actions
    participant Validation as Validation Scripts
    participant Orchestration as apply-changes.sh
    participant SyncScript as sync-*.sh
    participant JiraAPI as Jira REST API

    GitHub->>Workflow: Push to main with .jira-config changes
    Workflow->>Validation: Run validate-configs.sh
    Validation->>Validation: Check schemas & duplicates
    Validation-->>Workflow: Validation complete
    
    Workflow->>Orchestration: Run apply-changes.sh
    Orchestration->>Orchestration: Detect changed files via git diff
    Orchestration->>SyncScript: Execute sync-boards.sh (if boards.json changed)
    SyncScript->>SyncScript: Parse config, build payloads
    SyncScript->>JiraAPI: PUT /rest/api/2/board/{id}/properties
    JiraAPI-->>SyncScript: HTTP 200/error response
    SyncScript-->>Orchestration: Report success/failure
    
    Orchestration->>SyncScript: Execute sync-filters.sh (if filters.json changed)
    SyncScript->>JiraAPI: PUT /rest/api/2/filter/{id}
    JiraAPI-->>SyncScript: HTTP response
    SyncScript-->>Orchestration: Report result
    
    Orchestration->>SyncScript: Execute sync-projects.sh (if projects.json changed)
    SyncScript->>JiraAPI: PUT /rest/api/2/project/{key}
    JiraAPI-->>SyncScript: HTTP response
    SyncScript-->>Orchestration: Report result
    
    Orchestration->>SyncScript: Execute sync-components.sh (if components.json changed)
    SyncScript->>JiraAPI: PUT /rest/api/2/component/{id}
    JiraAPI-->>SyncScript: HTTP response
    SyncScript-->>Orchestration: Report result
    
    Orchestration-->>Workflow: Exit with aggregate status
    Workflow-->>GitHub: Workflow complete
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands and usage tips.

@jeff-roche
Copy link
Copy Markdown
Contributor Author

/hold for review and secrets

@openshift-ci openshift-ci Bot added the do-not-merge/hold Indicates that a PR should not merge because someone has issued a /hold command. label Mar 25, 2026
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 12

🧹 Nitpick comments (4)
edge-scrum/.jira-config/boards.json (1)

13-19: Empty properties object — confirm this is intentional.

The "OCPEDGE Scrum" board has an empty properties object, while the other two boards explicitly configure roadmap and child-issue planning properties.

If default board settings are appropriate for this board, consider adding a comment to make this explicit. If properties should be configured, please add them for consistency.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@edge-scrum/.jira-config/boards.json` around lines 13 - 19, The "OCPEDGE
Scrum" board entry (id "8557", name "OCPEDGE Scrum") has an empty properties
object; either make this explicit by adding a comment indicating default
settings are intentional or align it with the other boards by populating the
properties field with the same roadmap/child-issue planning keys used elsewhere
(copy the properties structure from the other board entries and adjust values as
needed) so the configuration is consistent.
.github/workflows/jira-sync-pr.yml (1)

36-48: Document why credentials are required for dry-run and explain fork PR behavior.

The dry-run job requires Jira credentials because the sync scripts validate JIRA_USERNAME and JIRA_API_TOKEN upfront (even though actual API requests aren't made in dry-run mode). This means:

  1. PRs from forks won't have access to secrets and will fail at the credential validation step
  2. The error message is clear (JIRA_USERNAME must be set), but the reason isn't obvious

Consider adding a workflow comment or README section explaining why credentials are validated even for dry-run previews and noting that fork PRs will fail at this step due to GitHub's security model for secrets access.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/jira-sync-pr.yml around lines 36 - 48, Add documentation
near the dry-run job (job name "dry-run") explaining that the script
edge-scrum/.ci/scripts/apply-changes.sh performs upfront validation of
JIRA_USERNAME and JIRA_API_TOKEN even in --dry-run mode, and therefore GitHub
Actions secrets are required; also note that PRs from forks will not have access
to these secrets and will fail at the credential validation step due to GitHub's
secrets security model. Update the workflow file with a brief inline comment
above the env block for JIRA_USERNAME/JIRA_API_TOKEN and/or add a short note to
the repo README/CI docs describing this fork-PR behavior and suggested
alternatives (e.g., running the preview locally or enabling a bot account).
edge-scrum/.ci/scripts/apply-changes.sh (1)

10-10: Remove unused JIRA_CONFIG_DIR variable.

JIRA_CONFIG_DIR is declared on Line 10 but never used; dropping it will keep the script tighter and avoid ShellCheck noise.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@edge-scrum/.ci/scripts/apply-changes.sh` at line 10, Remove the unused shell
variable by deleting the JIRA_CONFIG_DIR declaration
(JIRA_CONFIG_DIR="${EDGE_SCRUM_DIR}/.jira-config") since it is never referenced
elsewhere; ensure no other code relies on JIRA_CONFIG_DIR and run a quick
ShellCheck to confirm no remaining warnings related to this variable.
edge-scrum/.ci/scripts/validate-readme-sync.sh (1)

10-10: Drop unused variables in README sync validator.

README_FILE (Line 10) and file_path (Line 43) are not read afterward; removing them will reduce dead code and ShellCheck warnings.

Also applies to: 43-43

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@edge-scrum/.ci/scripts/validate-readme-sync.sh` at line 10, Remove the dead
variables by deleting the README_FILE assignment and the unused file_path
variable in the validate-readme-sync.sh script; search for README_FILE and
file_path and remove their declarations and any dead references (or replace with
the correct variable/path if they were meant to be used), then run ShellCheck to
confirm warnings are gone and the script still functions with the remaining
variables and logic.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@edge-scrum/.ci/README.md`:
- Around line 124-142: The README currently instructs contributors to create and
load credentials from edge-scrum/.env, but the CI scripts read from the process
environment and do not source that file; update the docs to remove the cat >
edge-scrum/.env example and the claim “Load from edge-scrum/.env” and instead
instruct users to export the required environment variables (JIRA_URL,
JIRA_USERNAME, JIRA_API_TOKEN) into their shell or CI secret store so the
scripts that read process env vars will see them; keep wording consistent with
the PR’s env-only secret handling and mention the exact variable names
(JIRA_URL, JIRA_USERNAME, JIRA_API_TOKEN) referenced in the scripts.

In `@edge-scrum/.ci/schemas/filters.schema.json`:
- Around line 4-29: The permission schema currently only enforces required
fields when permission.type === "group", letting invalid shapes for other types
(user, project, role, authenticated, global) pass validation and fail later;
update the "permission" definition to validate each variant explicitly by adding
additional conditional branches (if/then) or a oneOf that ties type values to
their required properties (e.g., when type === "user" require a "user" object
with its expected fields, when type === "project" require a "project" object,
when type === "role" require a "role" object, and for "authenticated"/"global"
disallow extraneous variant objects), and ensure extraneous properties are
disallowed for each variant so sync-filters.sh receives correctly-shaped
payloads (refer to the "permission" object, its "type" property, and the
existing if/then logic to extend).

In `@edge-scrum/.ci/schemas/projects.schema.json`:
- Around line 45-47: The schema currently marks "roles" as required but only
declares "type": "array" without any item schema and the sync logic (see
projects.json sync) ignores roles; either remove the "roles" property and its
required entry from projects.schema.json if roles are not implemented, or
replace the loose declaration with a proper items definition (e.g., an array of
objects with required fields like "name", "actor", "accountId"/"account_id", and
any allowed enums) and update any corresponding validation code used by the sync
script so the structure is enforced and actually applied during sync (refer to
the "roles" property and the sync handling in projects.json to keep names
consistent).

In `@edge-scrum/.ci/scripts/sync-boards.sh`:
- Around line 121-126: The curl call that sets HTTP_CODE in sync-boards.sh can
block indefinitely; update the invocation that writes to
"${TMPDIR}/response.json" to include connection and overall timeouts and retry
controls (e.g., --connect-timeout, --max-time, --retry, --retry-delay and
--retry-connrefused) so PUTs to
"${JIRA_URL}/rest/agile/1.0/board/${BOARD_ID}/properties/${PROPERTY_KEY}" using
${JIRA_AUTH} and ${PROPERTY_VALUE} will fail fast and retry transient errors
instead of hanging the CI job.

In `@edge-scrum/.ci/scripts/sync-components.sh`:
- Line 76: Replace fragile jq array iteration that uses '.components[]' /
'.projects[]' (seen in the COMPONENTS and PROJECTS assignments and the other jq
calls) with a safe, empty-aware form so jq won't fail on empty arrays;
specifically change occurrences like COMPONENTS=$(jq -c '.components[]'
"${CONFIG_FILE}") and PROJECTS=$(jq -c '.projects[]' ...) to use the safe
operator '?', e.g. COMPONENTS=$(jq -c '.components[]?' "${CONFIG_FILE}") and
PROJECTS=$(jq -c '.projects[]?' "${CONFIG_FILE}"), and apply the same change to
the other jq invocations around the mentioned regions (lines handling
components/projects) to prevent jq from exiting with an error on empty input.
- Around line 128-133: The curl PUT that sets HTTP_CODE can hang; update the
curl invocation that uses UPDATE_PAYLOAD, COMPONENT_ID and JIRA_URL (the command
assigning HTTP_CODE) to include network hardening: set connection and overall
timeouts (e.g., --connect-timeout and --max-time), enable automatic retries for
transient failures (e.g., --retry, --retry-delay and --retry-connrefused) and
fail fast on HTTP errors (e.g., --fail) so the CI job won’t block indefinitely
and will surface transient errors reliably.

In `@edge-scrum/.ci/scripts/sync-filters.sh`:
- Line 207: The post-increment expression used to advance the loop counter (the
((index++)) expression) returns a non-zero exit status under set -euo pipefail
on the first iteration and aborts the script; replace that post-increment with a
form that yields a zero exit status (e.g., a pre-increment or an explicit
addition assignment) so the loop can continue, updating the single use of the
index increment expression in the loop where index is advanced.
- Around line 140-145: The curl call that sets `response=$(curl -s -w
"\n%{http_code}" -X PUT ... -d "$payload"
"${JIRA_URL}/rest/api/2/filter/${filter_id}")` needs bounded timeouts and retry
behavior to avoid hangs; update this invocation to include connect and overall
timeouts (e.g., --connect-timeout and --max-time), enable controlled retries
(e.g., --retry N, --retry-delay SECONDS and/or --retry-max-time), and surface
errors (e.g., --fail --show-error) so transient network failures are retried and
permanent failures return a non-zero exit code; after the call, check curl's
exit status and the HTTP status code in `response` and handle retry/failure
accordingly.

In `@edge-scrum/.ci/scripts/sync-projects.sh`:
- Around line 149-154: The curl call that assigns response in sync-projects.sh
currently has no timeout or retry controls and can hang; update the curl
invocation that assigns the response variable to include connection and total
timeouts and retries by adding the flags --connect-timeout 10 --max-time 60
--retry 3 --retry-all-errors so the PUT to
"${JIRA_URL}/rest/api/2/project/${project_key}" (using JIRA_AUTH and payload)
fails fast and retries transient errors.

In `@edge-scrum/.ci/scripts/validate-configs.sh`:
- Around line 82-86: The duplicate-name check is missing for
boards/filters/projects: update the validation calls so each of boards.json,
filters.json and projects.json is also checked for unique "name" values (in
addition to "id") by invoking the same validation routine used for
components/labels; specifically add calls to validate_file for
"${CONFIG_DIR}/boards.json", "${CONFIG_DIR}/filters.json", and
"${CONFIG_DIR}/projects.json" with the corresponding schema variables and "name"
as the uniqueness key (same pattern used for components.json/labels.json), or
extend the validate_file implementation to accept and run uniqueness checks for
both "id" and "name" when present.

In `@edge-scrum/.jira-config/filters.json`:
- Line 227: The JQL in the "jql" string incorrectly mixes OR and AND without
grouping, so the assignment/status constraints only apply to the last filter;
update the query in the jql value to add explicit parentheses so the AND clause
applies to all intended sources (wrap the OR-connected sources — e.g., project
in (USHIFT, OCPEDGE) OR filter = "OpenShift Edge - Core Backlog" OR filter in
("OpenShift Edge - External Projects", "OpenShift Edge - Bugs and CVEs") — in
parens, then append AND (filter = "OpenShift Edge - Team Assigned" OR (filter =
"OpenShift Edge - QE Assigned" AND status in (...))) to ensure correct
precedence).

In `@edge-scrum/.jira-config/projects.json`:
- Around line 14-153: projects.json contains a large "roles" block that is not
synced by the tooling (sync-projects.sh acknowledges this and update_project()
only syncs name/description/leadAccountId/assigneeType), causing configuration
drift and confusion; either remove the entire "roles" field from projects.json
and move role setup instructions into README.md, or add a prominent inline
comment above the "roles" array in projects.json stating "UI-only — not managed
by sync-projects.sh / update_project()" and document the manual Jira UI steps in
README.md (ensure README.md cross-references sync-projects.sh and
update_project()).

---

Nitpick comments:
In @.github/workflows/jira-sync-pr.yml:
- Around line 36-48: Add documentation near the dry-run job (job name "dry-run")
explaining that the script edge-scrum/.ci/scripts/apply-changes.sh performs
upfront validation of JIRA_USERNAME and JIRA_API_TOKEN even in --dry-run mode,
and therefore GitHub Actions secrets are required; also note that PRs from forks
will not have access to these secrets and will fail at the credential validation
step due to GitHub's secrets security model. Update the workflow file with a
brief inline comment above the env block for JIRA_USERNAME/JIRA_API_TOKEN and/or
add a short note to the repo README/CI docs describing this fork-PR behavior and
suggested alternatives (e.g., running the preview locally or enabling a bot
account).

In `@edge-scrum/.ci/scripts/apply-changes.sh`:
- Line 10: Remove the unused shell variable by deleting the JIRA_CONFIG_DIR
declaration (JIRA_CONFIG_DIR="${EDGE_SCRUM_DIR}/.jira-config") since it is never
referenced elsewhere; ensure no other code relies on JIRA_CONFIG_DIR and run a
quick ShellCheck to confirm no remaining warnings related to this variable.

In `@edge-scrum/.ci/scripts/validate-readme-sync.sh`:
- Line 10: Remove the dead variables by deleting the README_FILE assignment and
the unused file_path variable in the validate-readme-sync.sh script; search for
README_FILE and file_path and remove their declarations and any dead references
(or replace with the correct variable/path if they were meant to be used), then
run ShellCheck to confirm warnings are gone and the script still functions with
the remaining variables and logic.

In `@edge-scrum/.jira-config/boards.json`:
- Around line 13-19: The "OCPEDGE Scrum" board entry (id "8557", name "OCPEDGE
Scrum") has an empty properties object; either make this explicit by adding a
comment indicating default settings are intentional or align it with the other
boards by populating the properties field with the same roadmap/child-issue
planning keys used elsewhere (copy the properties structure from the other board
entries and adjust values as needed) so the configuration is consistent.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: f8d2386e-92af-4172-908b-af241a110599

📥 Commits

Reviewing files that changed from the base of the PR and between c20dd33 and 72e9376.

📒 Files selected for processing (23)
  • .github/workflows/jira-sync-apply.yml
  • .github/workflows/jira-sync-pr.yml
  • .gitignore
  • edge-scrum/.ci/README.md
  • edge-scrum/.ci/schemas/boards.schema.json
  • edge-scrum/.ci/schemas/components.schema.json
  • edge-scrum/.ci/schemas/filters.schema.json
  • edge-scrum/.ci/schemas/labels.schema.json
  • edge-scrum/.ci/schemas/projects.schema.json
  • edge-scrum/.ci/scripts/apply-changes.sh
  • edge-scrum/.ci/scripts/sync-boards.sh
  • edge-scrum/.ci/scripts/sync-components.sh
  • edge-scrum/.ci/scripts/sync-filters.sh
  • edge-scrum/.ci/scripts/sync-projects.sh
  • edge-scrum/.ci/scripts/validate-configs.sh
  • edge-scrum/.ci/scripts/validate-readme-sync.sh
  • edge-scrum/.jira-config/README.md
  • edge-scrum/.jira-config/boards.json
  • edge-scrum/.jira-config/components.json
  • edge-scrum/.jira-config/filters.json
  • edge-scrum/.jira-config/labels.json
  • edge-scrum/.jira-config/projects.json
  • edge-scrum/README.md

Comment thread edge-scrum/.ci/README.md
Comment on lines +124 to +142
- **Credentials:** Load from `edge-scrum/.env` (JIRA_URL, JIRA_USERNAME, JIRA_API_TOKEN)
- **Error handling:** Exit with code 1 if any updates fail
- **Validation:** Check prerequisites (jq, curl, config files, env vars)
- **Logging:** Clear, color-coded output showing what's being updated
- **Safety:** Only update existing entities (no create/delete operations)

## Prerequisites

```bash
# Install dependencies
sudo dnf install jq curl git # Fedora/RHEL
sudo apt install jq curl git # Debian/Ubuntu

# Configure credentials
cat > edge-scrum/.env <<EOF
export JIRA_URL="https://redhat.atlassian.net"
export JIRA_USERNAME="your-email@redhat.com"
export JIRA_API_TOKEN="your-api-token"
EOF
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Credential setup docs are inconsistent with script behavior and PR security model.

At Line 124 and Line 138-Line 142, the README says credentials are loaded from .env, but these scripts read process environment variables and do not source that file. This can mislead contributors and conflicts with the env-only secret handling described in this PR.

📝 Suggested documentation correction
-- **Credentials:** Load from `edge-scrum/.env` (JIRA_URL, JIRA_USERNAME, JIRA_API_TOKEN)
+- **Credentials:** Provide as environment variables (JIRA_URL, JIRA_USERNAME, JIRA_API_TOKEN)

@@
-# Configure credentials
-cat > edge-scrum/.env <<EOF
-export JIRA_URL="https://redhat.atlassian.net"
-export JIRA_USERNAME="your-email@redhat.com"
-export JIRA_API_TOKEN="your-api-token"
-EOF
+# Configure credentials for current shell/session
+export JIRA_URL="https://redhat.atlassian.net"
+export JIRA_USERNAME="your-email@redhat.com"
+export JIRA_API_TOKEN="your-api-token"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@edge-scrum/.ci/README.md` around lines 124 - 142, The README currently
instructs contributors to create and load credentials from edge-scrum/.env, but
the CI scripts read from the process environment and do not source that file;
update the docs to remove the cat > edge-scrum/.env example and the claim “Load
from edge-scrum/.env” and instead instruct users to export the required
environment variables (JIRA_URL, JIRA_USERNAME, JIRA_API_TOKEN) into their shell
or CI secret store so the scripts that read process env vars will see them; keep
wording consistent with the PR’s env-only secret handling and mention the exact
variable names (JIRA_URL, JIRA_USERNAME, JIRA_API_TOKEN) referenced in the
scripts.

Comment on lines +4 to +29
"permission": {
"type": "object",
"required": ["type"],
"properties": {
"type": {
"type": "string",
"enum": ["group", "user", "authenticated", "project", "role", "global"]
},
"group": {
"type": "object",
"required": ["name"],
"properties": {
"name": { "type": "string", "minLength": 1 }
}
},
"user": {
"type": "object"
}
},
"if": {
"properties": { "type": { "const": "group" } }
},
"then": {
"required": ["group"]
}
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Harden permission variant validation to prevent CI-pass/runtime-fail payloads.

At Line 4-Line 29, permission accepts multiple type values but only enforces required fields for group. That allows invalid user/project/role shapes through schema validation, then fails later when sync-filters.sh sends them to Jira.

🔧 Proposed schema tightening
 "permission": {
   "type": "object",
+  "additionalProperties": false,
   "required": ["type"],
   "properties": {
     "type": {
       "type": "string",
       "enum": ["group", "user", "authenticated", "project", "role", "global"]
     },
     "group": {
       "type": "object",
+      "additionalProperties": false,
       "required": ["name"],
       "properties": {
         "name": { "type": "string", "minLength": 1 }
       }
     },
-    "user": {
-      "type": "object"
-    }
-  },
-  "if": {
-    "properties": { "type": { "const": "group" } }
-  },
-  "then": {
-    "required": ["group"]
-  }
+    "user": { "type": "object", "required": ["accountId"], "properties": { "accountId": { "type": "string", "minLength": 1 } }, "additionalProperties": false },
+    "project": { "type": "object", "required": ["id"], "properties": { "id": { "type": "string", "pattern": "^[0-9]+$" } }, "additionalProperties": false },
+    "role": { "type": "object", "required": ["id"], "properties": { "id": { "type": "string", "pattern": "^[0-9]+$" } }, "additionalProperties": false }
+  },
+  "allOf": [
+    { "if": { "properties": { "type": { "const": "group" } } }, "then": { "required": ["group"] } },
+    { "if": { "properties": { "type": { "const": "user" } } }, "then": { "required": ["user"] } },
+    { "if": { "properties": { "type": { "const": "project" } } }, "then": { "required": ["project"] } },
+    { "if": { "properties": { "type": { "const": "role" } } }, "then": { "required": ["role"] } }
+  ]
 }
📝 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
"permission": {
"type": "object",
"required": ["type"],
"properties": {
"type": {
"type": "string",
"enum": ["group", "user", "authenticated", "project", "role", "global"]
},
"group": {
"type": "object",
"required": ["name"],
"properties": {
"name": { "type": "string", "minLength": 1 }
}
},
"user": {
"type": "object"
}
},
"if": {
"properties": { "type": { "const": "group" } }
},
"then": {
"required": ["group"]
}
}
"permission": {
"type": "object",
"additionalProperties": false,
"required": ["type"],
"properties": {
"type": {
"type": "string",
"enum": ["group", "user", "authenticated", "project", "role", "global"]
},
"group": {
"type": "object",
"additionalProperties": false,
"required": ["name"],
"properties": {
"name": { "type": "string", "minLength": 1 }
}
},
"user": { "type": "object", "required": ["accountId"], "properties": { "accountId": { "type": "string", "minLength": 1 } }, "additionalProperties": false },
"project": { "type": "object", "required": ["id"], "properties": { "id": { "type": "string", "pattern": "^[0-9]+$" } }, "additionalProperties": false },
"role": { "type": "object", "required": ["id"], "properties": { "id": { "type": "string", "pattern": "^[0-9]+$" } }, "additionalProperties": false }
},
"allOf": [
{ "if": { "properties": { "type": { "const": "group" } } }, "then": { "required": ["group"] } },
{ "if": { "properties": { "type": { "const": "user" } } }, "then": { "required": ["user"] } },
{ "if": { "properties": { "type": { "const": "project" } } }, "then": { "required": ["project"] } },
{ "if": { "properties": { "type": { "const": "role" } } }, "then": { "required": ["role"] } }
]
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@edge-scrum/.ci/schemas/filters.schema.json` around lines 4 - 29, The
permission schema currently only enforces required fields when permission.type
=== "group", letting invalid shapes for other types (user, project, role,
authenticated, global) pass validation and fail later; update the "permission"
definition to validate each variant explicitly by adding additional conditional
branches (if/then) or a oneOf that ties type values to their required properties
(e.g., when type === "user" require a "user" object with its expected fields,
when type === "project" require a "project" object, when type === "role" require
a "role" object, and for "authenticated"/"global" disallow extraneous variant
objects), and ensure extraneous properties are disallowed for each variant so
sync-filters.sh receives correctly-shaped payloads (refer to the "permission"
object, its "type" property, and the existing if/then logic to extend).

Comment on lines +45 to +47
"roles": {
"type": "array"
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Roles field required but not validated or synced.

The schema requires roles (line 12) but defines it only as "type": "array" with no structure validation. This is problematic because:

  1. No validation: Users can put arbitrary data in the roles array without any schema constraints on role objects, actors, account IDs, etc.
  2. Not synced: The sync script ignores roles entirely (see projects.json review comment).
  3. Misleading: Marking as required suggests it's important, but it's neither validated nor applied.

This compounds the issue flagged in projects.json — the role configuration appears to be managed through config-as-code but is actually non-functional.

Recommended fix: Align the schema with the actual sync behavior:

Option 1: Remove roles from schema (if not implementing sync)
-        "required": ["id", "key", "name", "description", "assigneeType", "lead", "roles"],
+        "required": ["id", "key", "name", "description", "assigneeType", "lead"],
         "additionalProperties": false,
         "properties": {
           "id": {
             "type": "string",
             "pattern": "^[0-9]+$"
           },
           ...
           "lead": {
             "type": "object",
             "required": ["id", "email"],
             "properties": {
               "id": { "type": "string", "minLength": 1 },
               "email": { "type": "string", "pattern": ".+@.+" }
             }
-          },
-          "roles": {
-            "type": "array"
           }
         }
Option 2: Properly validate roles structure (if keeping for reference)
           "roles": {
-            "type": "array"
+            "type": "array",
+            "items": {
+              "type": "object",
+              "required": ["id", "name"],
+              "properties": {
+                "id": { "type": "string" },
+                "name": { "type": "string" },
+                "description": { "type": "string" },
+                "actors": {
+                  "type": "array",
+                  "items": {
+                    "type": "object",
+                    "required": ["type"],
+                    "properties": {
+                      "type": { "type": "string" },
+                      "account_id": { "type": "string" },
+                      "group_id": { "type": "string" },
+                      "display_name": { "type": "string" },
+                      "group_name": { "type": "string" }
+                    }
+                  }
+                }
+              }
+            }
           }
📝 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
"roles": {
"type": "array"
}
"roles": {
"type": "array",
"items": {
"type": "object",
"required": ["id", "name"],
"properties": {
"id": { "type": "string" },
"name": { "type": "string" },
"description": { "type": "string" },
"actors": {
"type": "array",
"items": {
"type": "object",
"required": ["type"],
"properties": {
"type": { "type": "string" },
"account_id": { "type": "string" },
"group_id": { "type": "string" },
"display_name": { "type": "string" },
"group_name": { "type": "string" }
}
}
}
}
}
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@edge-scrum/.ci/schemas/projects.schema.json` around lines 45 - 47, The schema
currently marks "roles" as required but only declares "type": "array" without
any item schema and the sync logic (see projects.json sync) ignores roles;
either remove the "roles" property and its required entry from
projects.schema.json if roles are not implemented, or replace the loose
declaration with a proper items definition (e.g., an array of objects with
required fields like "name", "actor", "accountId"/"account_id", and any allowed
enums) and update any corresponding validation code used by the sync script so
the structure is enforced and actually applied during sync (refer to the "roles"
property and the sync handling in projects.json to keep names consistent).

Comment on lines +121 to +126
HTTP_CODE=$(curl -s -w "%{http_code}" -o "${TMPDIR}/response.json" \
-X PUT \
-H "Content-Type: application/json" \
-H "Authorization: Basic ${JIRA_AUTH}" \
-d "${PROPERTY_VALUE}" \
"${JIRA_URL}/rest/agile/1.0/board/${BOARD_ID}/properties/${PROPERTY_KEY}")
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Add timeout/retry controls to Jira API calls.

Line 121 currently allows indefinite blocking on network stalls. In CI automation, this can wedge the whole sync job.

Suggested hardening for curl invocation
-            HTTP_CODE=$(curl -s -w "%{http_code}" -o "${TMPDIR}/response.json" \
+            HTTP_CODE=$(curl -sS -w "%{http_code}" -o "${TMPDIR}/response.json" \
+                --connect-timeout 10 \
+                --max-time 60 \
+                --retry 3 \
+                --retry-delay 2 \
+                --retry-connrefused \
                 -X PUT \
                 -H "Content-Type: application/json" \
                 -H "Authorization: Basic ${JIRA_AUTH}" \
                 -d "${PROPERTY_VALUE}" \
                 "${JIRA_URL}/rest/agile/1.0/board/${BOARD_ID}/properties/${PROPERTY_KEY}")
📝 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
HTTP_CODE=$(curl -s -w "%{http_code}" -o "${TMPDIR}/response.json" \
-X PUT \
-H "Content-Type: application/json" \
-H "Authorization: Basic ${JIRA_AUTH}" \
-d "${PROPERTY_VALUE}" \
"${JIRA_URL}/rest/agile/1.0/board/${BOARD_ID}/properties/${PROPERTY_KEY}")
HTTP_CODE=$(curl -sS -w "%{http_code}" -o "${TMPDIR}/response.json" \
--connect-timeout 10 \
--max-time 60 \
--retry 3 \
--retry-delay 2 \
--retry-connrefused \
-X PUT \
-H "Content-Type: application/json" \
-H "Authorization: Basic ${JIRA_AUTH}" \
-d "${PROPERTY_VALUE}" \
"${JIRA_URL}/rest/agile/1.0/board/${BOARD_ID}/properties/${PROPERTY_KEY}")
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@edge-scrum/.ci/scripts/sync-boards.sh` around lines 121 - 126, The curl call
that sets HTTP_CODE in sync-boards.sh can block indefinitely; update the
invocation that writes to "${TMPDIR}/response.json" to include connection and
overall timeouts and retry controls (e.g., --connect-timeout, --max-time,
--retry, --retry-delay and --retry-connrefused) so PUTs to
"${JIRA_URL}/rest/agile/1.0/board/${BOARD_ID}/properties/${PROPERTY_KEY}" using
${JIRA_AUTH} and ${PROPERTY_VALUE} will fail fast and retry transient errors
instead of hanging the CI job.

UPDATE_FAILED=false

# Read components from config file
COMPONENTS=$(jq -c '.components[]' "${CONFIG_FILE}")
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Empty arrays can trigger jq parse failures and abort the sync.

With set -euo pipefail, using COMPONENTS=$(jq -c '.components[]' ...) / PROJECTS=$(jq -c '.projects[]') plus here-strings can execute one empty iteration and make jq fail on blank input.

Suggested robust iteration pattern
-COMPONENTS=$(jq -c '.components[]' "${CONFIG_FILE}")
+COMPONENTS_CMD=(jq -c '.components[]?' "${CONFIG_FILE}")
@@
-while IFS= read -r component; do
+while IFS= read -r component; do
     COMPONENT_NAME=$(echo "${component}" | jq -r '.name')
     COMPONENT_DESC=$(echo "${component}" | jq -r '.description')
@@
-    PROJECTS=$(echo "${component}" | jq -c '.projects[]')
-
-    while IFS= read -r project; do
+    while IFS= read -r project; do
         COMPONENT_ID=$(echo "${project}" | jq -r '.component_id // empty')
         PROJECT_KEY=$(echo "${project}" | jq -r '.project_key')
@@
-    done <<< "${PROJECTS}"
+    done < <(jq -c '.projects[]?' <<< "${component}")
@@
-done <<< "${COMPONENTS}"
+done < <("${COMPONENTS_CMD[@]}")

Also applies to: 92-92, 100-103, 145-149

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@edge-scrum/.ci/scripts/sync-components.sh` at line 76, Replace fragile jq
array iteration that uses '.components[]' / '.projects[]' (seen in the
COMPONENTS and PROJECTS assignments and the other jq calls) with a safe,
empty-aware form so jq won't fail on empty arrays; specifically change
occurrences like COMPONENTS=$(jq -c '.components[]' "${CONFIG_FILE}") and
PROJECTS=$(jq -c '.projects[]' ...) to use the safe operator '?', e.g.
COMPONENTS=$(jq -c '.components[]?' "${CONFIG_FILE}") and PROJECTS=$(jq -c
'.projects[]?' "${CONFIG_FILE}"), and apply the same change to the other jq
invocations around the mentioned regions (lines handling components/projects) to
prevent jq from exiting with an error on empty input.

exit_code=1
fi

((index++))
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify the risky increment pattern exists
rg -n '\(\(index\+\+\)\)' edge-scrum/.ci/scripts/sync-filters.sh

# Reproduce bash behavior under set -e (expected: early non-zero)
if bash -c 'set -e; i=0; ((i++)); echo "UNEXPECTED_REACHABLE"'; then
  echo "Unexpected: command did not fail"
  exit 1
else
  echo "Expected: post-increment returned non-zero and aborted under set -e"
fi

Repository: openshift-eng/edge-tooling

Length of output: 161


🏁 Script executed:

# Check if set -euo pipefail is in the script
head -20 edge-scrum/.ci/scripts/sync-filters.sh | grep -n "set"

# Get context around line 207 (10 lines before and after)
sed -n '197,217p' edge-scrum/.ci/scripts/sync-filters.sh | cat -n | sed 's/^[[:space:]]*//' | awk '{print NR+196 ": " $0}'

Repository: openshift-eng/edge-tooling

Length of output: 1043


🏁 Script executed:

# Get broader context to see the loop initialization
sed -n '180,210p' edge-scrum/.ci/scripts/sync-filters.sh | cat -n | awk '{print NR+179 ": " $0}'

Repository: openshift-eng/edge-tooling

Length of output: 1531


set -e + ((index++)) terminates the script on the first loop iteration.

At Line 207, post-increment in a (( )) command evaluates to 0 on the first iteration, causing an exit code of 1 under set -euo pipefail (set at line 19). This aborts the script before it can process any filters.

🐛 Safe increment fix
-        ((index++))
+        ((++index))

Pre-increment (++index) returns 1 on the first iteration, yielding exit code 0 and allowing the loop to continue.

📝 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
((index++))
((++index))
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@edge-scrum/.ci/scripts/sync-filters.sh` at line 207, The post-increment
expression used to advance the loop counter (the ((index++)) expression) returns
a non-zero exit status under set -euo pipefail on the first iteration and aborts
the script; replace that post-increment with a form that yields a zero exit
status (e.g., a pre-increment or an explicit addition assignment) so the loop
can continue, updating the single use of the index increment expression in the
loop where index is advanced.

Comment on lines +149 to +154
response=$(curl -s -w "\n%{http_code}" \
-X PUT \
-H "Content-Type: application/json" \
-H "Authorization: Basic ${JIRA_AUTH}" \
-d "${payload}" \
"${JIRA_URL}/rest/api/2/project/${project_key}")
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify timeout/retry flags are currently absent/present in this file
rg -n 'curl -s -w "\\n%\\{http_code\\}"' edge-scrum/.ci/scripts/sync-projects.sh
rg -n -- '--connect-timeout|--max-time|--retry|--retry-all-errors' edge-scrum/.ci/scripts/sync-projects.sh

Repository: openshift-eng/edge-tooling

Length of output: 203


🏁 Script executed:

#!/bin/bash
# Read the specific lines from the file
echo "=== Lines 149-154 from sync-projects.sh ==="
sed -n '149,154p' edge-scrum/.ci/scripts/sync-projects.sh

echo -e "\n=== All curl commands in sync-projects.sh ==="
rg -n 'curl' edge-scrum/.ci/scripts/sync-projects.sh -A 3

echo -e "\n=== Checking for timeout flags anywhere in sync-projects.sh ==="
rg -n 'connect-timeout|max-time|--retry' edge-scrum/.ci/scripts/sync-projects.sh

echo -e "\n=== Total file length ==="
wc -l edge-scrum/.ci/scripts/sync-projects.sh

Repository: openshift-eng/edge-tooling

Length of output: 952


Bind project update calls with timeout and retry controls.

At lines 149-154, this external Jira API write has no timeout. A stuck network path can block the workflow run indefinitely. Add --connect-timeout 10 --max-time 60 --retry 3 --retry-all-errors flags to the curl command:

🔧 Proposed fix
-    response=$(curl -s -w "\n%{http_code}" \
+    response=$(curl -s -w "\n%{http_code}" \
+        --connect-timeout 10 \
+        --max-time 60 \
+        --retry 3 \
+        --retry-all-errors \
         -X PUT \
         -H "Content-Type: application/json" \
         -H "Authorization: Basic ${JIRA_AUTH}" \
         -d "${payload}" \
         "${JIRA_URL}/rest/api/2/project/${project_key}")
📝 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
response=$(curl -s -w "\n%{http_code}" \
-X PUT \
-H "Content-Type: application/json" \
-H "Authorization: Basic ${JIRA_AUTH}" \
-d "${payload}" \
"${JIRA_URL}/rest/api/2/project/${project_key}")
response=$(curl -s -w "\n%{http_code}" \
--connect-timeout 10 \
--max-time 60 \
--retry 3 \
--retry-all-errors \
-X PUT \
-H "Content-Type: application/json" \
-H "Authorization: Basic ${JIRA_AUTH}" \
-d "${payload}" \
"${JIRA_URL}/rest/api/2/project/${project_key}")
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@edge-scrum/.ci/scripts/sync-projects.sh` around lines 149 - 154, The curl
call that assigns response in sync-projects.sh currently has no timeout or retry
controls and can hang; update the curl invocation that assigns the response
variable to include connection and total timeouts and retries by adding the
flags --connect-timeout 10 --max-time 60 --retry 3 --retry-all-errors so the PUT
to "${JIRA_URL}/rest/api/2/project/${project_key}" (using JIRA_AUTH and payload)
fails fast and retries transient errors.

Comment on lines +82 to +86
validate_file "${CONFIG_DIR}/boards.json" "${SCHEMA_DIR}/boards.schema.json" "boards" "id"
validate_file "${CONFIG_DIR}/filters.json" "${SCHEMA_DIR}/filters.schema.json" "filters" "id"
validate_file "${CONFIG_DIR}/projects.json" "${SCHEMA_DIR}/projects.schema.json" "projects" "id"
validate_file "${CONFIG_DIR}/components.json" "${SCHEMA_DIR}/components.schema.json" "components" "name"
validate_file "${CONFIG_DIR}/labels.json" "${SCHEMA_DIR}/labels.schema.json" "labels" "name"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Duplicate-name validation is missing for several config types.

Lines 82-84 validate only id uniqueness for boards/filters/projects, so duplicate name values can slip through even though the PR objective calls for duplicate ID/name checks.

Suggested approach
-    validate_file "${CONFIG_DIR}/boards.json"     "${SCHEMA_DIR}/boards.schema.json"     "boards"     "id"
-    validate_file "${CONFIG_DIR}/filters.json"    "${SCHEMA_DIR}/filters.schema.json"    "filters"    "id"
-    validate_file "${CONFIG_DIR}/projects.json"   "${SCHEMA_DIR}/projects.schema.json"   "projects"   "id"
+    validate_file "${CONFIG_DIR}/boards.json"     "${SCHEMA_DIR}/boards.schema.json"     "boards"     "id"
+    validate_file "${CONFIG_DIR}/boards.json"     "${SCHEMA_DIR}/boards.schema.json"     "boards"     "name"
+    validate_file "${CONFIG_DIR}/filters.json"    "${SCHEMA_DIR}/filters.schema.json"    "filters"    "id"
+    validate_file "${CONFIG_DIR}/filters.json"    "${SCHEMA_DIR}/filters.schema.json"    "filters"    "name"
+    validate_file "${CONFIG_DIR}/projects.json"   "${SCHEMA_DIR}/projects.schema.json"   "projects"   "id"
+    validate_file "${CONFIG_DIR}/projects.json"   "${SCHEMA_DIR}/projects.schema.json"   "projects"   "name"
📝 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
validate_file "${CONFIG_DIR}/boards.json" "${SCHEMA_DIR}/boards.schema.json" "boards" "id"
validate_file "${CONFIG_DIR}/filters.json" "${SCHEMA_DIR}/filters.schema.json" "filters" "id"
validate_file "${CONFIG_DIR}/projects.json" "${SCHEMA_DIR}/projects.schema.json" "projects" "id"
validate_file "${CONFIG_DIR}/components.json" "${SCHEMA_DIR}/components.schema.json" "components" "name"
validate_file "${CONFIG_DIR}/labels.json" "${SCHEMA_DIR}/labels.schema.json" "labels" "name"
validate_file "${CONFIG_DIR}/boards.json" "${SCHEMA_DIR}/boards.schema.json" "boards" "id"
validate_file "${CONFIG_DIR}/boards.json" "${SCHEMA_DIR}/boards.schema.json" "boards" "name"
validate_file "${CONFIG_DIR}/filters.json" "${SCHEMA_DIR}/filters.schema.json" "filters" "id"
validate_file "${CONFIG_DIR}/filters.json" "${SCHEMA_DIR}/filters.schema.json" "filters" "name"
validate_file "${CONFIG_DIR}/projects.json" "${SCHEMA_DIR}/projects.schema.json" "projects" "id"
validate_file "${CONFIG_DIR}/projects.json" "${SCHEMA_DIR}/projects.schema.json" "projects" "name"
validate_file "${CONFIG_DIR}/components.json" "${SCHEMA_DIR}/components.schema.json" "components" "name"
validate_file "${CONFIG_DIR}/labels.json" "${SCHEMA_DIR}/labels.schema.json" "labels" "name"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@edge-scrum/.ci/scripts/validate-configs.sh` around lines 82 - 86, The
duplicate-name check is missing for boards/filters/projects: update the
validation calls so each of boards.json, filters.json and projects.json is also
checked for unique "name" values (in addition to "id") by invoking the same
validation routine used for components/labels; specifically add calls to
validate_file for "${CONFIG_DIR}/boards.json", "${CONFIG_DIR}/filters.json", and
"${CONFIG_DIR}/projects.json" with the corresponding schema variables and "name"
as the uniqueness key (same pattern used for components.json/labels.json), or
extend the validate_file implementation to accept and run uniqueness checks for
both "id" and "name" when present.

"id": "104882",
"name": "OpenShift Edge - Scrum Board",
"description": "",
"jql": "project in (USHIFT, OCPEDGE) OR filter = \"OpenShift Edge - Core Backlog\" OR filter in (\"OpenShift Edge - External Projects\", \"OpenShift Edge - Bugs and CVEs\") AND (filter = \"OpenShift Edge - Team Assigned\" OR (filter = \"OpenShift Edge - QE Assigned\" AND status in (Review, MODIFIED, ON_QA, Testing))) ORDER BY Rank ASC",
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Potentially incorrect JQL precedence in Scrum Board filter.

Line 227 mixes OR and AND without outer grouping, so assignment/status constraints are not consistently applied to all included sources. This can materially change board scope.

Suggested JQL grouping fix
-      "jql": "project in (USHIFT, OCPEDGE) OR filter = \"OpenShift Edge - Core Backlog\" OR filter in (\"OpenShift Edge - External Projects\", \"OpenShift Edge - Bugs and CVEs\") AND (filter = \"OpenShift Edge - Team Assigned\" OR (filter = \"OpenShift Edge - QE Assigned\" AND status in (Review, MODIFIED, ON_QA, Testing))) ORDER BY Rank ASC",
+      "jql": "(project in (USHIFT, OCPEDGE) OR filter = \"OpenShift Edge - Core Backlog\" OR filter in (\"OpenShift Edge - External Projects\", \"OpenShift Edge - Bugs and CVEs\")) AND (filter = \"OpenShift Edge - Team Assigned\" OR (filter = \"OpenShift Edge - QE Assigned\" AND status in (Review, MODIFIED, ON_QA, Testing))) ORDER BY Rank ASC",
📝 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
"jql": "project in (USHIFT, OCPEDGE) OR filter = \"OpenShift Edge - Core Backlog\" OR filter in (\"OpenShift Edge - External Projects\", \"OpenShift Edge - Bugs and CVEs\") AND (filter = \"OpenShift Edge - Team Assigned\" OR (filter = \"OpenShift Edge - QE Assigned\" AND status in (Review, MODIFIED, ON_QA, Testing))) ORDER BY Rank ASC",
"jql": "(project in (USHIFT, OCPEDGE) OR filter = \"OpenShift Edge - Core Backlog\" OR filter in (\"OpenShift Edge - External Projects\", \"OpenShift Edge - Bugs and CVEs\")) AND (filter = \"OpenShift Edge - Team Assigned\" OR (filter = \"OpenShift Edge - QE Assigned\" AND status in (Review, MODIFIED, ON_QA, Testing))) ORDER BY Rank ASC",
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@edge-scrum/.jira-config/filters.json` at line 227, The JQL in the "jql"
string incorrectly mixes OR and AND without grouping, so the assignment/status
constraints only apply to the last filter; update the query in the jql value to
add explicit parentheses so the AND clause applies to all intended sources (wrap
the OR-connected sources — e.g., project in (USHIFT, OCPEDGE) OR filter =
"OpenShift Edge - Core Backlog" OR filter in ("OpenShift Edge - External
Projects", "OpenShift Edge - Bugs and CVEs") — in parens, then append AND
(filter = "OpenShift Edge - Team Assigned" OR (filter = "OpenShift Edge - QE
Assigned" AND status in (...))) to ensure correct precedence).

Comment on lines +14 to +153
"roles": [
{
"id": "10002",
"name": "Administrators",
"description": "A project role that represents administrators in a project",
"actors": [
{
"type": "atlassian-user-role-actor",
"account_id": "712020:93a6a685-d36f-4660-a64a-6870675c2ec8",
"display_name": "Chad Scribner"
},
{
"type": "atlassian-user-role-actor",
"account_id": "712020:eca54813-6101-4bb6-b169-5b91a02029a1",
"display_name": "Jeff Roche"
},
{
"type": "atlassian-user-role-actor",
"account_id": "70121:fd715ae6-c353-45e3-ab6d-4d856939baac",
"display_name": "Jeremy Poulin"
},
{
"type": "atlassian-user-role-actor",
"account_id": "712020:188ebd21-393a-42a0-82dc-4646bc713e61",
"display_name": "Pablo Acevedo Montserrat"
},
{
"type": "atlassian-user-role-actor",
"account_id": "61e811135fcc37006876d93a",
"display_name": "Gregory Giguashvili"
},
{
"type": "atlassian-user-role-actor",
"account_id": "604a3e8543eac6006f8fbe82",
"display_name": "Nicole Wilker"
},
{
"type": "atlassian-user-role-actor",
"account_id": "604bf97ce19f910069714b6c",
"display_name": "Sarah Kyros"
},
{
"type": "atlassian-user-role-actor",
"account_id": "712020:06373c2b-9dbb-4310-b3c9-05b75997831c",
"display_name": "Suleyman Akbas"
},
{
"type": "atlassian-group-role-actor",
"group_id": "21adff1a-e3a7-434f-a9e0-ad57c4f70762",
"group_name": "jira-administrators"
}
]
},
{
"id": "10125",
"name": "Users",
"description": "Standard user access",
"actors": []
},
{
"id": "10126",
"name": "Developers",
"description": "This project role is intended for anyone who is a team member, actively working in the Jira project. Developers cannot access the project settings, comments, and attachments that are not their own. They also do not have the ability to manage components, releases, or sprint information in the project. Developers do have the ability to start and complete sprints.",
"actors": [
{
"type": "atlassian-user-role-actor",
"account_id": "712020:07290b97-3f2b-4fe2-81a5-d5d501f306fe",
"display_name": "Edge JiraBot"
},
{
"type": "atlassian-user-role-actor",
"account_id": "712020:510de16d-fc42-48cf-8412-8b5cb72dc6bd",
"display_name": "Jira-SD-Elements-Integration Bot"
},
{
"type": "atlassian-group-role-actor",
"group_id": "70b1c436-6bfe-4027-bc2d-78ec147d8cb1",
"group_name": "OpenShift Jira Bots"
},
{
"type": "atlassian-group-role-actor",
"group_id": "13bd0387-d75c-4c18-9d37-5439e8bf984c",
"group_name": "Red Hat Employee"
}
]
},
{
"id": "10127",
"name": "Scrum master",
"description": "This role is intended for users that assist with day-to-day operation of the team but don't require full project administration access. Scrum masters, program managers, and others may find themselves in this role. It provides access to create, edit, start, and end sprints.",
"actors": [
{
"type": "atlassian-user-role-actor",
"account_id": "712020:eca54813-6101-4bb6-b169-5b91a02029a1",
"display_name": "Jeff Roche"
},
{
"type": "atlassian-group-role-actor",
"group_id": "4937a28d-eae0-4776-990c-589b162a7fe7",
"group_name": "openshift-scrummaster-role"
}
]
},
{
"id": "10128",
"name": "Approver",
"description": "Approval permissions",
"actors": []
},
{
"id": "10129",
"name": "Viewers",
"description": "Read-only access",
"actors": []
},
{
"id": "10162",
"name": "Version Manager",
"description": "Manages versions and releases",
"actors": []
},
{
"id": "10056",
"name": "Service Desk Customers",
"description": "Service desk customer access",
"actors": []
},
{
"id": "10057",
"name": "Service Desk Team",
"description": "Service desk team members",
"actors": []
},
{
"id": "10003",
"name": "atlassian-addons-project-access",
"description": "Atlassian add-on access",
"actors": []
}
]
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify whether Jira API supports role actor management

echo "Checking Jira API documentation for project role endpoints..."

# Check if there are alternative endpoints for managing role actors
rg -n "role" edge-scrum/.ci/scripts/sync-projects.sh -A 5 -B 5

Repository: openshift-eng/edge-tooling

Length of output: 551


🏁 Script executed:

# Get more context around the role handling in the sync script
sed -n '200,240p' edge-scrum/.ci/scripts/sync-projects.sh

Repository: openshift-eng/edge-tooling

Length of output: 1170


🏁 Script executed:

# Check if there are other references to roles in the sync script
rg -n "role" edge-scrum/.ci/scripts/sync-projects.sh

Repository: openshift-eng/edge-tooling

Length of output: 96


🏁 Script executed:

# Check for documentation in README or related files about role management
find . -name "README*" -o -name "*.md" | head -20 | xargs grep -l "role" 2>/dev/null

Repository: openshift-eng/edge-tooling

Length of output: 190


🏁 Script executed:

# Check the full structure of the sync script to see what functions are called for project updates
rg -n "update_project|sync" edge-scrum/.ci/scripts/sync-projects.sh | head -20

Repository: openshift-eng/edge-tooling

Length of output: 299


🏁 Script executed:

# Get the update_project function implementation
sed -n '111,160p' edge-scrum/.ci/scripts/sync-projects.sh

Repository: openshift-eng/edge-tooling

Length of output: 1595


🏁 Script executed:

# Read the README in .jira-config directory
cat edge-scrum/.jira-config/README.md

Repository: openshift-eng/edge-tooling

Length of output: 6339


🏁 Script executed:

# Check what fields are actually extracted from projects.json
rg -n "jq" edge-scrum/.ci/scripts/sync-projects.sh | head -20

Repository: openshift-eng/edge-tooling

Length of output: 640


Roles are not synced; document or remove the unused configuration.

Role assignments in projects.json (lines 14-153) are not applied during sync operations. The sync script explicitly acknowledges this limitation (sync-projects.sh lines 219-223) because the Jira REST API does not support role actor updates via the project endpoint. The update_project() function only syncs name, description, leadAccountId, and assigneeType—roles are never extracted or processed.

While the README.md already documents that roles are "UI-Only Configuration," having 140 lines of unused role configuration in projects.json creates:

  • Configuration drift risk: Roles can change in the Jira UI without the JSON reflecting those changes
  • Misleading maintenance burden: The presence of roles in the config file suggests they are managed by this sync process when they are not
  • Developer confusion: New team members may assume roles are synced and waste time troubleshooting

Either:

  1. Remove the roles field from projects.json and consolidate all role documentation in a separate manual setup guide in the README
  2. Add an inline comment within projects.json clarifying that roles are for reference only and must be managed via the Jira UI
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@edge-scrum/.jira-config/projects.json` around lines 14 - 153, projects.json
contains a large "roles" block that is not synced by the tooling
(sync-projects.sh acknowledges this and update_project() only syncs
name/description/leadAccountId/assigneeType), causing configuration drift and
confusion; either remove the entire "roles" field from projects.json and move
role setup instructions into README.md, or add a prominent inline comment above
the "roles" array in projects.json stating "UI-only — not managed by
sync-projects.sh / update_project()" and document the manual Jira UI steps in
README.md (ensure README.md cross-references sync-projects.sh and
update_project()).

@jeff-roche
Copy link
Copy Markdown
Contributor Author

jeff-roche commented Mar 26, 2026

Holding so I can address the coderabbit feedback (pretty much all valid points) and wait for agreement on Edge KR 1.1 - Unified Scrum

@jeff-roche jeff-roche marked this pull request as draft April 27, 2026 12:22
@openshift-ci openshift-ci Bot added the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Apr 27, 2026
@openshift-ci
Copy link
Copy Markdown

openshift-ci Bot commented May 1, 2026

@jeff-roche: The following tests failed, say /retest to rerun all failed tests or /retest-required to rerun all mandatory failed tests:

Test name Commit Details Required Rerun command
ci/prow/markdownlint 72e9376 link true /test markdownlint
ci/prow/shellcheck 72e9376 link true /test shellcheck

Full PR test history. Your PR dashboard.

Details

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository. I understand the commands that are listed here.

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

Labels

approved Indicates a PR has been approved by an approver from all required OWNERS files. do-not-merge/hold Indicates that a PR should not merge because someone has issued a /hold command. do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant