$npx -y skills add opendatahub-io/ai-helpers --skill jira-workitem-attachUpload file attachments to Jira tickets. Verifies file exists and uploads via Jira API. Use when user wants to attach files to tickets.
| 1 | # Jira Workitem Attach |
| 2 | |
| 3 | Upload file attachments to Jira tickets using the Jira REST API (since `acli` does not support attachment uploads). |
| 4 | |
| 5 | ## Prerequisites |
| 6 | |
| 7 | - `acli` must be installed and authenticated (`acli jira auth`) to verify the upload |
| 8 | - Python 3.10+ and `uv` must be installed |
| 9 | - `JIRA_API_TOKEN` environment variable must be set with a valid API token |
| 10 | - `JIRA_EMAIL` environment variable must be set with the email address associated with your Atlassian account |
| 11 | |
| 12 | **Security Note**: The `JIRA_API_TOKEN` is sensitive and should be kept secure: |
| 13 | - Never log or echo the token value |
| 14 | - Avoid running debug commands that print environment variables |
| 15 | - The upload script handles the token securely via HTTPS |
| 16 | - Do not share logs or command output that may contain the token |
| 17 | |
| 18 | ## Implementation |
| 19 | |
| 20 | ### Step 1: Verify ACLi Setup |
| 21 | |
| 22 | Before proceeding, verify that acli is installed and authenticated by invoking the `acli-setup-check` skill: |
| 23 | |
| 24 | ```text |
| 25 | /acli-setup-check |
| 26 | ``` |
| 27 | |
| 28 | If the setup check fails, stop execution and guide the user to fix the issue. |
| 29 | |
| 30 | ### Step 2: Verify Environment Variables |
| 31 | |
| 32 | Check that the required environment variables are set: |
| 33 | |
| 34 | ```bash |
| 35 | if [[ -z "$JIRA_API_TOKEN" || -z "$JIRA_EMAIL" ]]; then |
| 36 | echo "Error: JIRA_API_TOKEN and JIRA_EMAIL environment variables must be set" |
| 37 | exit 1 |
| 38 | fi |
| 39 | ``` |
| 40 | |
| 41 | If either variable is missing, inform the user: |
| 42 | |
| 43 | ```text |
| 44 | JIRA_API_TOKEN and JIRA_EMAIL environment variables are required for uploading attachments. |
| 45 | |
| 46 | To set them: |
| 47 | export JIRA_EMAIL="your-email@redhat.com" |
| 48 | export JIRA_API_TOKEN="your-api-token" |
| 49 | |
| 50 | Get your API token from: |
| 51 | https://id.atlassian.com/manage-profile/security/api-tokens |
| 52 | ``` |
| 53 | |
| 54 | Note: The Python upload script will also validate these variables, but checking early provides better user experience. |
| 55 | |
| 56 | ### Step 3: Determine Ticket Key and File Path |
| 57 | |
| 58 | 1. **Ticket Key**: |
| 59 | - If provided by user, use it |
| 60 | - Otherwise, search conversation history for ticket references matching the pattern `[A-Z]+-\d+` |
| 61 | - If not found, ask: "Which ticket should I attach the file to?" |
| 62 | |
| 63 | 2. **File Path**: |
| 64 | - If user provides a file path, validate it exists |
| 65 | - If user mentions a file by name, search for it in the current directory |
| 66 | - If unclear, ask: "Which file should I attach?" |
| 67 | |
| 68 | 3. **Validate File Path Security**: |
| 69 | |
| 70 | Before proceeding to Step 4, validate the FILE_PATH for security: |
| 71 | |
| 72 | ```bash |
| 73 | # Resolve to absolute path and check it exists |
| 74 | FILE_PATH="<user-provided-path>" |
| 75 | REAL_PATH=$(realpath -e "$FILE_PATH" 2>/dev/null) |
| 76 | if [[ $? -ne 0 ]]; then |
| 77 | echo "Error: File does not exist or is not accessible: $FILE_PATH" |
| 78 | exit 1 |
| 79 | fi |
| 80 | |
| 81 | # Check if file is readable |
| 82 | if [[ ! -r "$REAL_PATH" ]]; then |
| 83 | echo "Error: File is not readable: $FILE_PATH" |
| 84 | exit 1 |
| 85 | fi |
| 86 | |
| 87 | # Reject parent-traversal patterns in original path |
| 88 | if [[ "$FILE_PATH" == *".."* ]]; then |
| 89 | echo "Error: Parent directory traversal (..) not allowed in file path" |
| 90 | exit 1 |
| 91 | fi |
| 92 | |
| 93 | # Restrict to allowed directories: current working directory or /tmp |
| 94 | CURRENT_DIR=$(pwd) |
| 95 | if [[ "$REAL_PATH" != "$CURRENT_DIR"/* ]] && [[ "$REAL_PATH" != /tmp/* ]]; then |
| 96 | echo "Error: File must be in current directory or /tmp for security" |
| 97 | echo " File location: $REAL_PATH" |
| 98 | echo " Allowed: $CURRENT_DIR/* or /tmp/*" |
| 99 | exit 1 |
| 100 | fi |
| 101 | |
| 102 | # Check for symlinks pointing outside allowed directories |
| 103 | if [[ -L "$FILE_PATH" ]]; then |
| 104 | echo "Error: Symlinks not allowed for security reasons" |
| 105 | exit 1 |
| 106 | fi |
| 107 | ``` |
| 108 | |
| 109 | If any validation fails, surface the error to the user and abort before calling upload_attachment.py. |
| 110 | |
| 111 | ### Step 4: Upload Attachment |
| 112 | |
| 113 | Since `acli` does not support attachment uploads, use the upload |
| 114 | script located at `scripts/upload_attachment.py` relative to the |
| 115 | `jira-workitem-attach` skill directory. |
| 116 | |
| 117 | Resolve the skill directory path and execute the script directly (not via `python` or |
| 118 | `bash`) to invoke `uv` via the shebang: |
| 119 | |
| 120 | ```bash |
| 121 | # Resolve skill directory path (adjust based on runtime context) |
| 122 | # Option 1: If skill directory is known via environment or context |
| 123 | SKILL_DIR="<path-to-helpers>/helpers/skills/jira-workitem-attach" |
| 124 | |
| 125 | # Option 2: Compute relative to repository root if available |
| 126 | # SKILL_DIR="${REPO_ROOT}/helpers/skills/jira-workitem-attach" |
| 127 | |
| 128 | # Option 3: Use current directory if already in skill directory |
| 129 | # SKILL_DIR="$(pwd)" |
| 130 | |
| 131 | # Execute the upload script with resolved path |
| 132 | "$SKILL_DIR/scripts/upload_attachment.py" "$TICKET_KEY" "$FILE_PATH" |
| 133 | ``` |
| 134 | |
| 135 | Note: The skill directory path resolution method depends on how the runtime |
| 136 | provides context. Use whichever approach is available (environment variables, |
| 137 | repository-root relative paths, or explicit path configuration). |
| 138 | |
| 139 | The script will: |
| 140 | - Verify environment variables (JIRA_API_TOKEN, JIRA_EMAIL) are set |
| 141 | - Valida |