Created
November 1, 2025 02:29
-
-
Save apinter/3d8dbea4579d6999c50937b458a16961 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env python3 | |
| import argparse | |
| import os | |
| import sys | |
| import requests | |
| GITLAB_URL = os.getenv("GITLAB_URL") | |
| GITLAB_PRIVATE_TOKEN = os.getenv("GITLAB_PRIVATE_TOKEN") | |
| GITLAB_PROJECT_ID = os.getenv("GITLAB_PROJECT_ID") | |
| def check_env_vars(): | |
| if not all([GITLAB_URL, GITLAB_PRIVATE_TOKEN, GITLAB_PROJECT_ID]): | |
| print("Error: Please set the following environment variables:") | |
| print(" - GITLAB_URL: Your GitLab instance URL (e.g., https://gitlab.com)") | |
| print( | |
| " - GITLAB_PRIVATE_TOKEN: Your GitLab Personal Access Token with 'api' scope." | |
| ) | |
| print(" - GITLAB_PROJECT_ID: Your GitLab project ID.") | |
| sys.exit(1) | |
| def get_api_url(endpoint): | |
| return f"{GITLAB_URL}/api/v4/projects/{GITLAB_PROJECT_ID}{endpoint}" | |
| def handle_api_error(response): | |
| if not response.ok: | |
| print(f"Error: API request failed with status code {response.status_code}") | |
| try: | |
| print(f"Response: {response.json()}") | |
| except ValueError: | |
| print(f"Response: {response.text}") | |
| sys.exit(1) | |
| def select_secure_files(): | |
| files = list_secure_files(return_files=True) | |
| if not files: | |
| print("No secure files found to select from.") | |
| sys.exit(0) | |
| print("Please select files from the list below:") | |
| for i, f in enumerate(files): | |
| print(f" {i+1}: ID: {f['id']}, Name: {f['name']}") | |
| while True: | |
| try: | |
| selection = input( | |
| "Enter the numbers of the files to select (comma-separated), or 'all': " | |
| ) | |
| if selection.lower() == "all": | |
| return [f["id"] for f in files] | |
| selected_indices = [int(i.strip()) - 1 for i in selection.split(",")] | |
| if all(0 <= i < len(files) for i in selected_indices): | |
| return [files[i]["id"] for i in selected_indices] | |
| else: | |
| print("Invalid selection. Please enter numbers from the list.") | |
| except ValueError: | |
| print("Invalid input. Please enter numbers separated by commas.") | |
| def list_secure_files(return_files=False): | |
| url = get_api_url("/secure_files?per_page=2500") | |
| headers = {"PRIVATE-TOKEN": GITLAB_PRIVATE_TOKEN} | |
| response = requests.get(url, headers=headers) | |
| handle_api_error(response) | |
| files = response.json() | |
| if return_files: | |
| return files | |
| if not files: | |
| print("No secure files found in this project.") | |
| return | |
| print("Secure Files:") | |
| for f in files: | |
| print(f" - ID: {f['id']}, Name: {f['name']}, Created: {f['created_at']}") | |
| def download_secure_files(): | |
| file_ids = select_secure_files() | |
| if not file_ids: | |
| return | |
| print(f"Downloading {len(file_ids)} secure file(s)...") | |
| for file_id in file_ids: | |
| url = get_api_url(f"/secure_files/{file_id}/download") | |
| headers = {"PRIVATE-TOKEN": GITLAB_PRIVATE_TOKEN} | |
| with requests.get(url, headers=headers, stream=True) as r: | |
| handle_api_error(r) | |
| content_disposition = r.headers.get("content-disposition") | |
| if content_disposition: | |
| filename = content_disposition.split("filename=")[-1].strip('"') | |
| else: | |
| filename = f"secure_file_{file_id}" | |
| with open(filename, "wb") as f: | |
| for chunk in r.iter_content(chunk_size=8192): | |
| f.write(chunk) | |
| print(f" - Downloaded '{filename}'") | |
| def upload_secure_files(file_paths): | |
| print(f"Uploading {len(file_paths)} file(s)...") | |
| for file_path in file_paths: | |
| if not os.path.exists(file_path): | |
| print(f"Error: File not found at '{file_path}'. Skipping.") | |
| continue | |
| url = get_api_url("/secure_files") | |
| headers = {"PRIVATE-TOKEN": GITLAB_PRIVATE_TOKEN} | |
| file_name = os.path.basename(file_path) | |
| with open(file_path, "rb") as f: | |
| files = {"file": (file_name, f, "application/octet-stream")} | |
| data = {"name": file_name} | |
| response = requests.post(url, headers=headers, data=data, files=files) | |
| if response.status_code == 201: | |
| print(f" - Successfully uploaded '{file_name}'") | |
| else: | |
| handle_api_error(response) | |
| def delete_secure_files(): | |
| file_ids = select_secure_files() | |
| if not file_ids: | |
| return | |
| print(f"Deleting {len(file_ids)} secure file(s)...") | |
| for file_id in file_ids: | |
| url = get_api_url(f"/secure_files/{file_id}") | |
| headers = {"PRIVATE-TOKEN": GITLAB_PRIVATE_TOKEN} | |
| response = requests.delete(url, headers=headers) | |
| if response.status_code == 204: | |
| print(f" - Successfully deleted secure file with ID: {file_id}") | |
| else: | |
| handle_api_error(response) | |
| def main(): | |
| parser = argparse.ArgumentParser( | |
| description="A script to manage GitLab Secure Files via the API.", | |
| epilog="Make sure to set GITLAB_URL, GITLAB_PRIVATE_TOKEN, and GITLAB_PROJECT_ID environment variables.", | |
| ) | |
| subparsers = parser.add_subparsers( | |
| dest="command", required=True, help="Available commands" | |
| ) | |
| subparsers.add_parser("list", help="List all secure files.") | |
| subparsers.add_parser( | |
| "download", help="Download one or more secure files interactively." | |
| ) | |
| upload_parser = subparsers.add_parser("upload", help="Upload one or more files.") | |
| upload_parser.add_argument( | |
| "file_paths", nargs="+", help="Paths to one or more files to upload." | |
| ) | |
| subparsers.add_parser( | |
| "delete", help="Delete one or more secure files interactively." | |
| ) | |
| args = parser.parse_args() | |
| check_env_vars() | |
| if args.command == "list": | |
| list_secure_files() | |
| elif args.command == "download": | |
| download_secure_files() | |
| elif args.command == "upload": | |
| upload_secure_files(args.file_paths) | |
| elif args.command == "delete": | |
| delete_secure_files() | |
| if __name__ == "__main__": | |
| main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment