bytelyst-devops-tools/bytelyst-cli.sh
2025-06-23 21:59:14 -07:00

239 lines
8.8 KiB
Bash

#!/bin/bash
# bytelyst-cli.sh: Unified CLI for Bytelyst GitHub DevOps Tools
#
# Usage examples:
# ./bytelyst-cli.sh list-public-repos --user <username>
# ./bytelyst-cli.sh list-private-repos --org <orgname>
# ./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 <command> [options]"
echo "Commands:"
echo " list-public-repos --user <username>"
echo " list-private-repos --org <orgname>"
echo " check-collaborators --input <input.json>"
echo " export --type <repos|users> --output <file.json>"
echo " remove-user-from-all-repos --user <username> [--input <file.json>]"
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 <username>.${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 <orgname>.${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 <input.json> (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 <repos|users> and --output <file.json>.${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 <username>.${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