I'm bringing a new custom Python script to automate the deletion of attachments in Jira issues. The idea is to facilitate this key process when managing attachments in specific projects or in the whole Jira instance.
This script utilizes Jira Cloud REST API endpoints (like this one) to identify and delete attachments associated with specified issues. It processes a list of issue keys from either a CSV or TXT file, ensuring a smooth and effective attachment management experience. The script logs all activities, including successful deletions and any errors encountered, providing a comprehensive overview of the operation.
Here's what the script focuses on:
requests
library: pip install requests
import requests from requests.auth import HTTPBasicAuth from getpass import getpass import logging import os import csv from datetime import datetime logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') log_filename = f"jira_attachment_delete_{datetime.now().strftime('%Y%m%d_%H%M%S')}.log" file_handler = logging.FileHandler(log_filename) file_handler.setFormatter(logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')) logging.getLogger().addHandler(file_handler) def get_jira_auth(): jira_domain = input("Enter your Jira Cloud domain (e.g., your-domain.atlassian.net): ") email = input("Enter your Jira email: ") api_token = getpass("Enter your Jira API token: ") return jira_domain, HTTPBasicAuth(email, api_token) def read_issue_keys(file_path): issue_keys = [] if file_path.lower().endswith('.csv'): with open(file_path, 'r', newline='', encoding='utf-8-sig') as csvfile: reader = csv.DictReader(csvfile) lower_case_headers = {header.lower(): header for header in reader.fieldnames} issue_key_column = lower_case_headers.get('issue key') if not issue_key_column: raise KeyError("CSV file must contain a column named 'Issue Key' (case-insensitive).") for row in reader: issue_keys.append(row[issue_key_column]) elif file_path.lower().endswith('.txt'): with open(file_path, 'r', encoding='utf-8-sig') as txtfile: for line in txtfile: issue_keys.append(line.strip()) return issue_keys def delete_attachments(jira_domain, auth, file_path): base_url = f"https://{jira_domain}/rest/api/3/issue/" failed_deletions = [] total_attachments = 0 failed_attachments = 0 issue_keys = read_issue_keys(file_path) for issue_key in issue_keys: try: response = requests.get(f"{base_url}{issue_key}", auth=auth) response.raise_for_status() issue_data = response.json() attachments = issue_data.get('fields', {}).get('attachment', []) for attachment in attachments: total_attachments += 1 attachment_id = attachment['id'] attachment_filename = attachment['filename'] try: delete_url = f"https://{jira_domain}/rest/api/3/attachment/{attachment_id}" delete_response = requests.delete(delete_url, auth=auth) delete_response.raise_for_status() logging.info(f"Successfully deleted {attachment_filename} from issue {issue_key}.") except requests.exceptions.RequestException as e: failed_attachments += 1 failed_deletions.append((issue_key, attachment_filename, str(e))) logging.error(f"Failed to delete {attachment_filename} from issue {issue_key}: {e}") except requests.exceptions.RequestException as e: logging.error(f"Failed to retrieve issue {issue_key}: {e}") failed_deletions.append((issue_key, 'N/A', str(e))) logging.info(f"Deletion completed: {total_attachments - failed_attachments} attachments deleted successfully.") logging.info(f"{failed_attachments} attachments failed to delete from {len(failed_deletions)} issues.") if failed_deletions: with open('failed_deletions.log', 'w', encoding='utf-8') as log_file: for issue_key, filename, error in failed_deletions: log_file.write(f"Issue Key: {issue_key}, Filename: {filename}, Error: {error}\n") logging.info(f"Details of failed deletions logged in: failed_deletions.log") def main(): jira_domain, auth = get_jira_auth() while True: file_path = input(""" Enter the path to your CSV or TXT file. The file must contain a list of issue keys. - CSV files must have a column named "Issue Key". - TXT files must have one issue key per line. Examples: 1. CSV in the same directory as the script: issues.csv 2. TXT in a subdirectory called "data": data/issues.txt 3. Full path to the file: /path/to/your/issues.csv or /path/to/your/issues.txt File Path: """) if file_path.lower().endswith(('.csv', '.txt')): break else: print("Invalid file type. Please enter a CSV or TXT file.") delete_attachments(jira_domain, auth, file_path) if __name__ == "__main__": main()
This script is provided "as is" and "as available" without warranties and is not officially supported or endorsed by Atlassian. Use at your own risk. Jira API changes may impact functionality.
Cheers!
Delfino Rosales
Senior Cloud Support Engineer
Amsterdam, NL
2 comments