Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions .github/workflows/jira-sync-apply.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
name: Jira Config — Apply Changes

on:
push:
branches: [main]
paths:
- 'edge-scrum/.jira-config/**'
workflow_dispatch:

permissions:
contents: read

concurrency:
group: jira-sync-apply
cancel-in-progress: false

jobs:
validate-configs:
name: Validate JSON config schemas
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Install check-jsonschema
run: pip install check-jsonschema

- name: Validate .jira-config JSON files
run: edge-scrum/.ci/scripts/validate-configs.sh

apply:
name: Apply Jira config changes
needs: validate-configs
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Apply metadata changes to Jira
run: edge-scrum/.ci/scripts/apply-changes.sh
env:
JIRA_USERNAME: ${{ secrets.JIRA_USERNAME }}
JIRA_API_TOKEN: ${{ secrets.JIRA_API_TOKEN }}
GIT_BASE_SHA: ${{ github.event.before }}
SYNC_ALL: ${{ github.event_name == 'workflow_dispatch' }}
48 changes: 48 additions & 0 deletions .github/workflows/jira-sync-pr.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
name: Jira Config — PR Validation

on:
pull_request:
branches: [main]
paths:
- 'edge-scrum/.jira-config/**'

permissions:
contents: read

jobs:
validate-configs:
name: Validate JSON config schemas
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Install check-jsonschema
run: pip install check-jsonschema

- name: Validate .jira-config JSON files
run: edge-scrum/.ci/scripts/validate-configs.sh

validate-readme:
name: Validate README sync
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Validate README updated alongside config changes
run: edge-scrum/.ci/scripts/validate-readme-sync.sh

dry-run:
name: Dry-run Jira sync
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Preview changes
run: edge-scrum/.ci/scripts/apply-changes.sh --dry-run
env:
JIRA_USERNAME: ${{ secrets.JIRA_USERNAME }}
JIRA_API_TOKEN: ${{ secrets.JIRA_API_TOKEN }}
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
.env
**/.env
151 changes: 151 additions & 0 deletions edge-scrum/.ci/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
# CI Scripts

This directory contains CI/CD validation and sync scripts for the edge-scrum project.

## Validation Scripts

### validate-readme-sync.sh

Validates that changes to metadata JSON files in `.jira-config/` are reflected in the README.

**Purpose:** Ensures documentation stays in sync with configuration changes.

**Usage:**

```bash
# Run locally
.ci/scripts/validate-readme-sync.sh

# In GitHub Actions
- name: Validate README sync
run: ./edge-scrum/.ci/scripts/validate-readme-sync.sh
```

**Behavior:**

- Detects modifications to any of the following files:
- `boards.json`
- `filters.json`
- `projects.json`
- `components.json`
- `plans.json`
- `labels.json`
- If any metadata file changed, verifies that `README.md` was also updated
- Exits with error code 1 if README wasn't updated alongside metadata changes
- Exits with code 0 if no metadata changed or if both metadata and README changed

**Environment Variables:**

- `GITHUB_BASE_REF` - GitHub Actions PR base branch (auto-detected)
- `CI_MERGE_REQUEST_TARGET_BRANCH_NAME` - GitLab CI target branch (auto-detected)
- Falls back to comparing against `origin/main` for local development

## Sync Scripts

These scripts apply metadata changes from JSON files to Jira via REST API.

### apply-changes.sh

**Orchestration script** that detects changed metadata files and applies them to Jira.

**Purpose:** Main entry point for syncing metadata changes to Jira.

**Usage:**

```bash
# Dry run (preview changes)
.ci/scripts/apply-changes.sh --dry-run

# Apply changes
.ci/scripts/apply-changes.sh

# In GitHub Actions
- name: Apply metadata changes to Jira
run: ./edge-scrum/.ci/scripts/apply-changes.sh
env:
JIRA_URL: ${{ secrets.JIRA_URL }}
JIRA_USERNAME: ${{ secrets.JIRA_USERNAME }}
JIRA_API_TOKEN: ${{ secrets.JIRA_API_TOKEN }}
```

**Behavior:**

- Detects which metadata files changed (boards.json, filters.json, projects.json, components.json)
- Calls the appropriate sync script for each changed file
- Passes `--dry-run` flag to all sync scripts if provided
- Exits with code 1 if any sync script fails
- Skips `labels.json` (no formal API)

### sync-boards.sh

Updates board properties in Jira.

**API:** `PUT /rest/agile/1.0/board/{boardId}/properties/{propertyKey}`

**Updates:** Board properties (e.g., roadmaps features, child issue planning)

**Note:** Only properties can be updated via API. Structural configuration (columns, estimation, ranking) requires Jira UI.

### sync-filters.sh

Updates filter metadata, JQL, and permissions in Jira.

**API:** `PUT /rest/api/2/filter/{filterId}`

**Updates:** Filter name, description, JQL, edit permissions, share permissions

**Note:** Filter ownership transfers require Jira UI.

### sync-projects.sh

Updates project metadata in Jira.

**API:** `PUT /rest/api/2/project/{projectKey}`

**Updates:** Project name, description, lead, assigneeType

**Note:** Role configuration is listed in JSON but is UI-only (cannot be updated via API).

### sync-components.sh

Updates component metadata in Jira.

**API:** `PUT /rest/api/2/component/{componentId}`

**Updates:** Component name, description

**Note:** Only updates components with `component_id` in the JSON. Entries without `component_id` must be created manually.

## Common Features

All sync scripts share these features:

- **`--dry-run` flag:** Preview changes without applying them
- **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
Comment on lines +124 to +142
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.

```

## Workflow

1. **Make changes** to metadata JSON files in `.jira-config/`
2. **Update README.md** with the changes (validated by `validate-readme-sync.sh`)
3. **Preview changes** with `apply-changes.sh --dry-run`
4. **Apply changes** with `apply-changes.sh`
5. **Commit and push** to create a PR
41 changes: 41 additions & 0 deletions edge-scrum/.ci/schemas/boards.schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"required": ["boards"],
"additionalProperties": false,
"properties": {
"boards": {
"type": "array",
"minItems": 1,
"items": {
"type": "object",
"required": ["id", "name", "type", "filter_id", "properties"],
"additionalProperties": false,
"properties": {
"id": {
"type": "string",
"pattern": "^[0-9]+$"
},
"name": {
"type": "string",
"minLength": 1
},
"type": {
"type": "string",
"enum": ["scrum", "kanban"]
},
"filter_id": {
"type": "string",
"pattern": "^[0-9]+$"
},
"properties": {
"type": "object",
"additionalProperties": {
"type": "boolean"
}
}
}
}
}
}
}
49 changes: 49 additions & 0 deletions edge-scrum/.ci/schemas/components.schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"required": ["components"],
"additionalProperties": false,
"properties": {
"components": {
"type": "array",
"minItems": 1,
"items": {
"type": "object",
"required": ["name", "description", "projects"],
"additionalProperties": false,
"properties": {
"name": {
"type": "string",
"minLength": 1
},
"description": {
"type": "string"
},
"projects": {
"type": "array",
"minItems": 1,
"items": {
"type": "object",
"required": ["project_id", "project_key"],
"additionalProperties": false,
"properties": {
"project_id": {
"type": "string",
"pattern": "^[0-9]+$"
},
"project_key": {
"type": "string",
"minLength": 1
},
"component_id": {
"type": "string",
"pattern": "^[0-9]+$"
}
}
}
}
}
}
}
}
}
Loading