#!/bin/bash # bytelyst-cli.sh: Unified CLI for Bytelyst GitHub DevOps Tools # # Usage examples: # ./bytelyst-cli.sh list-public-repos --user # ./bytelyst-cli.sh list-private-repos --org # ./bytelyst-cli.sh check-collaborators --input input.json # ./bytelyst-cli.sh export --type repos --output repos.json # # If no arguments are given, an interactive menu is shown. RED=$(tput setaf 1) GREEN=$(tput setaf 2) YELLOW=$(tput setaf 3) BLUE=$(tput setaf 4) RESET=$(tput sgr0) REQUIRED_TOOLS=(jq curl) # Check for required tools for tool in "${REQUIRED_TOOLS[@]}"; do if ! command -v $tool &>/dev/null; then echo "${RED}❌ Error: Required tool '$tool' is not installed. Please install it and try again.${RESET}" exit 1 fi done # Load .env if present if [[ -f .env ]]; then export $(grep -v '^#' .env | xargs) fi # Validate GITHUB_TOKEN if [[ -z "$GITHUB_TOKEN" ]]; then echo "${RED}❌ Error: GITHUB_TOKEN is not set.\nSet it in your environment (e.g., export GITHUB_TOKEN=... in ~/.zshrc, ~/.bashrc, or .env).${RESET}" exit 1 fi usage() { echo "${BLUE}Bytelyst CLI - Unified GitHub DevOps Tool${RESET}" echo "" echo "Usage: $0 [options]" echo "Commands:" echo " list-public-repos --user " echo " list-private-repos --org " echo " check-collaborators --input " echo " export --type --output " echo " remove-user-from-all-repos --user [--input ]" echo " help Show this help message" echo "" echo "If no command is given, an interactive menu will be shown." } list_public_repos() { local user="" while [[ $# -gt 0 ]]; do case $1 in --user) user="$2"; shift 2;; *) shift;; esac done if [[ -z "$user" ]]; then echo "${RED}❌ Please provide --user .${RESET}"; exit 1 fi echo "${BLUE}🔍 Fetching all public repositories for user: $user...${RESET}" local response=$(curl -s -H "Authorization: token $GITHUB_TOKEN" "https://api.github.com/users/$user/repos?per_page=100&type=public") local repos=$(echo "$response" | jq -r '.[].full_name') if [[ -z "$repos" ]]; then echo "${YELLOW}🚫 No public repositories found for user.${RESET}" else echo "$repos" fi } list_private_repos() { local org="" while [[ $# -gt 0 ]]; do case $1 in --org) org="$2"; shift 2;; *) shift;; esac done if [[ -z "$org" ]]; then echo "${RED}❌ Please provide --org .${RESET}"; exit 1 fi echo "${BLUE}🔍 Fetching all private repositories for org: $org...${RESET}" local response=$(curl -s -H "Authorization: token $GITHUB_TOKEN" "https://api.github.com/orgs/$org/repos?per_page=100&type=private") local repos=$(echo "$response" | jq -r '.[].full_name') if [[ -z "$repos" ]]; then echo "${YELLOW}🚫 No private repositories found for org.${RESET}" else echo "$repos" fi } check_collaborators() { local input="" while [[ $# -gt 0 ]]; do case $1 in --input) input="$2"; shift 2;; *) shift;; esac done if [[ -z "$input" || ! -f "$input" ]]; then echo "${RED}❌ Please provide --input (file must exist).${RESET}"; exit 1 fi local org=$(jq -r '.org' "$input") local user=$(jq -r '.user' "$input") local whitelist=($(jq -r '.whitelist[]' "$input")) local repos=($(jq -r '.repos[]' "$input")) if [[ -z "$org" || -z "$user" || ${#whitelist[@]} -eq 0 || ${#repos[@]} -eq 0 ]]; then echo "${RED}❌ input.json must contain 'org', 'user', 'whitelist', and 'repos'.${RESET}"; exit 1 fi for repo in "${repos[@]}"; do echo "${BLUE}🔍 Checking repo: $repo${RESET}" local collaborators=$(curl -s -H "Authorization: token $GITHUB_TOKEN" "https://api.github.com/repos/$org/$repo/collaborators" | jq -r '.[].login') local non_whitelisted=() for collab in $collaborators; do if [[ ! " ${whitelist[@]} " =~ " ${collab} " ]]; then non_whitelisted+=("$collab") fi done if [[ ${#non_whitelisted[@]} -gt 0 ]]; then echo "${YELLOW}🚨 Repository: $repo${RESET}" echo "${RED}❌ Non-Whitelisted Collaborators:${RESET}" printf '%s\n' "${non_whitelisted[@]}" for user in "${non_whitelisted[@]}"; do read -p "Do you want to remove collaborator '$user' from '$repo'? (yes/no): " confirm if [[ "$confirm" == "yes" ]]; then response=$(curl -s -o /dev/null -w "%{http_code}" -X DELETE -H "Authorization: token $GITHUB_TOKEN" "https://api.github.com/repos/$org/$repo/collaborators/$user") if [[ "$response" -eq 204 ]]; then echo "${GREEN}✅ Successfully removed $user from repository $repo.${RESET}" else echo "${YELLOW}⚠️ Failed to remove $user from repository $repo (HTTP Status: $response).${RESET}" fi else echo "${YELLOW}🚫 Skipped removal of $user from $repo.${RESET}" fi done echo "--------------------------------------------" fi done } export_json() { local type=""; local output="" while [[ $# -gt 0 ]]; do case $1 in --type) type="$2"; shift 2;; --output) output="$2"; shift 2;; *) shift;; esac done if [[ -z "$type" || -z "$output" ]]; then echo "${RED}❌ Please provide --type and --output .${RESET}"; exit 1 fi if [[ "$type" == "repos" ]]; then jq . repos.json > "$output" echo "${GREEN}✅ Exported repos to $output${RESET}" elif [[ "$type" == "users" ]]; then jq . users.json > "$output" echo "${GREEN}✅ Exported users to $output${RESET}" else echo "${RED}❌ Unknown export type: $type${RESET}" exit 1 fi } remove_user_from_all_repos() { local user=""; local input="github_repos.json" while [[ $# -gt 0 ]]; do case $1 in --user) user="$2"; shift 2;; --input) input="$2"; shift 2;; *) shift;; esac done if [[ -z "$user" ]]; then echo "${RED}❌ Please provide --user .${RESET}"; exit 1 fi if [[ ! -f "$input" ]]; then echo "${RED}❌ Input file '$input' not found.${RESET}"; exit 1 fi local org=$(jq -r '.org' "$input") local repos=($(jq -r '.repos[]' "$input")) if [[ -z "$org" || ${#repos[@]} -eq 0 ]]; then echo "${RED}❌ Input file must contain 'org' and 'repos'.${RESET}"; exit 1 fi for repo in "${repos[@]}"; do echo "${BLUE}🔗 Removing user '$user' from repo: $repo${RESET}" response=$(curl -s -o /dev/null -w "%{http_code}" -X DELETE -H "Authorization: token $GITHUB_TOKEN" "https://api.github.com/repos/$org/$repo/collaborators/$user") if [[ "$response" -eq 204 ]]; then echo "${GREEN}✅ Successfully removed $user from $repo.${RESET}" elif [[ "$response" -eq 404 ]]; then echo "${YELLOW}⚠️ $user is not a collaborator on $repo or repo not found.${RESET}" else echo "${RED}❌ Failed to remove $user from $repo (HTTP Status: $response).${RESET}" fi done } interactive_menu() { echo "${BLUE}Bytelyst CLI Interactive Menu${RESET}" select opt in "List Public Repos" "List Private Repos" "Check Collaborators" "Export JSON" "Remove User from All Repos" "Exit"; do case $REPLY in 1) read -p "Enter GitHub username: " user; list_public_repos --user "$user";; 2) read -p "Enter GitHub org: " org; list_private_repos --org "$org";; 3) read -p "Enter path to input.json: " input; check_collaborators --input "$input";; 4) read -p "Export type (repos/users): " type; read -p "Output file: " output; export_json --type "$type" --output "$output";; 5) read -p "Enter GitHub username: " user; remove_user_from_all_repos --user "$user";; 6) exit 0;; *) echo "Invalid option.";; esac done } # Main CLI dispatcher if [[ $# -eq 0 ]]; then interactive_menu exit 0 fi case $1 in list-public-repos) shift; list_public_repos "$@";; list-private-repos) shift; list_private_repos "$@";; check-collaborators) shift; check_collaborators "$@";; export) shift; export_json "$@";; remove-user-from-all-repos) shift; remove_user_from_all_repos "$@";; help|--help|-h) usage;; *) echo "${RED}Unknown command: $1${RESET}"; usage; exit 1;; esac