For the following issue: - https://github.com/fleetdm/fleet/issues/41419 <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Chores** * Updated tooling and documentation wording across scripts, utilities, and infrastructure files to reflect the product terminology change from “teams” to “fleets” (references now note “fleets” and indicate the former “teams” naming). <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Rachael Shaw <r@rachael.wtf>
336 lines
11 KiB
Bash
Executable File
336 lines
11 KiB
Bash
Executable File
#!/bin/bash
|
|
|
|
# GitOps Migration Tool
|
|
# Moves self_service, categories, labels_exclude_any, labels_include_any keys
|
|
# from software YAML files to fleet (formerly "teams") YAML files
|
|
#
|
|
# Usage: ./migrate.sh <teams_directory_path>
|
|
# Example: ./migrate.sh it-and-security/teams
|
|
|
|
set -euo pipefail
|
|
|
|
# Colors for output
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[1;33m'
|
|
BLUE='\033[0;34m'
|
|
NC='\033[0m' # No Color
|
|
|
|
# Global counters
|
|
PROCESSED_TEAMS=0
|
|
PROCESSED_PACKAGES=0
|
|
ERRORS=0
|
|
|
|
# Array to track software files that have been processed
|
|
PROCESSED_SOFTWARE_FILES=()
|
|
|
|
# Check if yq is installed
|
|
check_dependencies() {
|
|
if ! command -v yq &> /dev/null; then
|
|
echo -e "${RED}Error: yq is required but not installed. Please install yq first.${NC}"
|
|
echo "Install with: brew install yq"
|
|
exit 1
|
|
fi
|
|
|
|
# Check yq version (we need v4+)
|
|
YQ_VERSION=$(yq --version | cut -d' ' -f4 | cut -d'v' -f2 | cut -d'.' -f1)
|
|
if [[ "$YQ_VERSION" == "" ]]; then
|
|
YQ_VERSION="0"
|
|
fi
|
|
if [ "$YQ_VERSION" -lt 4 ]; then
|
|
echo -e "${RED}Error: yq version 4 or higher is required${NC}"
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
# Show usage information
|
|
show_usage() {
|
|
echo "Usage: $0 <teams_directory_path>"
|
|
echo
|
|
echo "Process YAML files in the specified teams directory, moving keys from"
|
|
echo "referenced software files to the team files."
|
|
echo
|
|
echo "Arguments:"
|
|
echo " teams_directory_path Path to directory containing team YAML files"
|
|
echo
|
|
echo "Examples:"
|
|
echo " $0 it-and-security/teams"
|
|
echo " $0 /path/to/teams"
|
|
echo
|
|
echo "Keys moved: self_service, categories, labels_include_any, labels_exclude_any"
|
|
}
|
|
|
|
# Validate YAML syntax
|
|
validate_yaml() {
|
|
local file="$1"
|
|
if ! yq eval '.' "$file" >/dev/null 2>&1; then
|
|
echo -e "${RED}Error: Invalid YAML syntax in $file${NC}"
|
|
return 1
|
|
fi
|
|
return 0
|
|
}
|
|
|
|
# Extract target keys from software file
|
|
extract_keys_from_software() {
|
|
local software_file="$1"
|
|
local temp_file=$(mktemp -p .)
|
|
chmod 666 $temp_file
|
|
|
|
# Extract the keys we need
|
|
{
|
|
echo "# Extracted keys from $software_file"
|
|
yq eval 'pick(["self_service", "categories", "labels_include_any", "labels_exclude_any"])' "$software_file" 2>/dev/null || echo "{}"
|
|
} > "$temp_file"
|
|
|
|
echo "$temp_file"
|
|
}
|
|
|
|
# Remove target keys from software file
|
|
remove_keys_from_software() {
|
|
local software_file="$1"
|
|
|
|
echo -e "${BLUE} Removing keys from: $software_file${NC}"
|
|
|
|
# Create a temporary file with keys removed
|
|
local temp_file=$(mktemp -p .)
|
|
chmod 666 $temp_file
|
|
yq eval --output-format=yaml 'del(.self_service, .categories, .labels_include_any, .labels_exclude_any)' "$software_file" > "$temp_file"
|
|
|
|
# Replace the original file
|
|
mv "$temp_file" "$software_file"
|
|
}
|
|
|
|
# Add keys to team file at specific package index
|
|
add_keys_to_team_file() {
|
|
local team_file="$1"
|
|
local package_index="$2"
|
|
local keys_file="$3"
|
|
|
|
# Check if keys file has any meaningful content
|
|
if ! yq eval 'keys | length > 0' "$keys_file" >/dev/null 2>&1; then
|
|
echo -e "${YELLOW} No keys to move${NC}"
|
|
return 0
|
|
fi
|
|
|
|
echo -e "${BLUE} Adding keys to team file at package index $package_index${NC}"
|
|
|
|
|
|
# Process each key type directly on the team file to preserve formatting
|
|
for key in "self_service" "categories" "labels_include_any" "labels_exclude_any"; do
|
|
if yq eval "has(\"$key\")" "$keys_file" | grep -q "true"; then
|
|
# Use yq to properly extract and merge the value, preserving arrays and complex structures
|
|
if yq eval ".$key != null" "$keys_file" | grep -q "true"; then
|
|
yq eval -i ".software.packages[$package_index].$key = load(\"$keys_file\").$key" "$team_file"
|
|
echo -e "${GREEN} Added $key${NC}"
|
|
fi
|
|
fi
|
|
done
|
|
}
|
|
|
|
# Process a single team file (Pass 1: Add keys to team files only)
|
|
process_team_file() {
|
|
local team_file="$1"
|
|
echo -e "${GREEN}Processing team file: $team_file${NC}"
|
|
|
|
# Check if file has software.packages section
|
|
if ! yq eval 'has("software") and .software | has("packages")' "$team_file" | grep -q "true"; then
|
|
echo -e "${YELLOW} No software.packages section found, skipping${NC}"
|
|
return 0
|
|
fi
|
|
|
|
# Get the number of packages
|
|
local package_count=$(yq eval '.software.packages | length' "$team_file")
|
|
echo -e "${BLUE} Found $package_count packages${NC}"
|
|
|
|
# Process each package
|
|
for ((i=0; i<package_count; i++)); do
|
|
echo -e "${BLUE} Processing package $((i+1))/$package_count${NC}"
|
|
|
|
# Get the path from the package
|
|
local package_path=$(yq eval ".software.packages[$i].path" "$team_file")
|
|
|
|
if [ "$package_path" = "null" ]; then
|
|
echo -e "${YELLOW} No path found, skipping${NC}"
|
|
continue
|
|
fi
|
|
|
|
echo -e "${BLUE} Package path: $package_path${NC}"
|
|
|
|
# Convert relative path to absolute path
|
|
local team_dir=$(dirname "$team_file")
|
|
local software_file="$team_dir/$package_path"
|
|
|
|
# Normalize the path
|
|
software_file=$(realpath "$software_file" 2>/dev/null || echo "$software_file")
|
|
|
|
if [ ! -f "$software_file" ]; then
|
|
echo -e "${RED} Error: Software file not found: $software_file${NC}"
|
|
ERRORS=$((ERRORS+1))
|
|
continue
|
|
fi
|
|
|
|
# Validate software file
|
|
if ! validate_yaml "$software_file"; then
|
|
echo -e "${RED} Error: Invalid YAML in software file${NC}"
|
|
ERRORS=$((ERRORS+1))
|
|
continue
|
|
fi
|
|
|
|
echo -e "${BLUE} Processing: $software_file${NC}"
|
|
|
|
# Extract keys from software file
|
|
local keys_temp_file=$(extract_keys_from_software "$software_file")
|
|
|
|
# Add keys to team file
|
|
add_keys_to_team_file "$team_file" "$i" "$keys_temp_file"
|
|
|
|
# Track this software file for cleanup in pass 2
|
|
PROCESSED_SOFTWARE_FILES+=("$software_file")
|
|
|
|
# Clean up temp file
|
|
rm -f "$keys_temp_file"
|
|
|
|
PROCESSED_PACKAGED=$((PROCESSED_PACKAGES+1))
|
|
echo -e "${GREEN} ✓ Package processed successfully${NC}"
|
|
done
|
|
|
|
# Validate the modified team file
|
|
if ! validate_yaml "$team_file"; then
|
|
echo -e "${RED} Error: Team file became invalid after processing${NC}"
|
|
ERRORS=$((ERRORS+1))
|
|
return 1
|
|
fi
|
|
|
|
PROCESSED_TEAMS=$((PROCESSED_TEAMS+1))
|
|
echo -e "${GREEN}✓ Team file processed successfully${NC}"
|
|
echo
|
|
}
|
|
|
|
# Pass 2: Remove keys from all processed software files
|
|
cleanup_software_files() {
|
|
echo -e "${GREEN}=== PASS 2: CLEANING UP SOFTWARE FILES ===${NC}"
|
|
|
|
# Remove duplicates from the array
|
|
local unique_files=($(printf "%s\n" "${PROCESSED_SOFTWARE_FILES[@]}" | sort -u))
|
|
|
|
echo -e "${BLUE}Removing keys from ${#unique_files[@]} unique software files${NC}"
|
|
|
|
for software_file in "${unique_files[@]}"; do
|
|
echo -e "${BLUE} Removing keys from: $software_file${NC}"
|
|
remove_keys_from_software "$software_file"
|
|
done
|
|
|
|
echo -e "${GREEN}✓ Software file cleanup complete${NC}"
|
|
echo
|
|
}
|
|
# Fix Unicode escape sequences back to emoji characters
|
|
fix_unicode_emojis() {
|
|
local file="$1"
|
|
echo -e "${BLUE} Restoring emoji characters in: $(basename "$file")${NC}"
|
|
|
|
# Use perl to convert Unicode escape sequences back to actual characters
|
|
if command -v perl &> /dev/null; then
|
|
perl -i -pe 's/\\U([0-9A-F]{8})/chr(hex($1))/ge' "$file"
|
|
else
|
|
# Fallback: convert specific known emojis manually
|
|
sed -i '' 's/\\U0001F4BB/💻/g' "$file" 2>/dev/null || sed -i 's/\\U0001F4BB/💻/g' "$file"
|
|
sed -i '' 's/\\U0001F423/🐣/g' "$file" 2>/dev/null || sed -i 's/\\U0001F423/🐣/g' "$file"
|
|
fi
|
|
}
|
|
|
|
# Fix emojis in all processed team files
|
|
restore_emojis_in_team_files() {
|
|
echo -e "${GREEN}=== RESTORING EMOJI CHARACTERS ===${NC}"
|
|
|
|
for team_file in "${team_files[@]}"; do
|
|
if [ -f "$team_file" ] && grep -q "\\\\U[0-9A-F]" "$team_file"; then
|
|
fix_unicode_emojis "$team_file"
|
|
fi
|
|
done
|
|
|
|
echo -e "${GREEN}✓ Emoji restoration complete${NC}"
|
|
echo
|
|
}
|
|
|
|
|
|
# Main function
|
|
main() {
|
|
# Check if directory path argument is provided
|
|
if [ $# -eq 0 ]; then
|
|
echo -e "${RED}Error: No teams directory path provided${NC}"
|
|
echo
|
|
show_usage
|
|
exit 1
|
|
fi
|
|
|
|
# Handle help flags
|
|
if [ "$1" = "-h" ] || [ "$1" = "--help" ]; then
|
|
show_usage
|
|
exit 0
|
|
fi
|
|
|
|
local teams_dir="$1"
|
|
|
|
echo -e "${GREEN}GitOps Migration Tool${NC}"
|
|
echo -e "${BLUE}Moving keys from software files to team files${NC}"
|
|
echo -e "${BLUE}Teams directory: $teams_dir${NC}"
|
|
echo
|
|
|
|
# Check dependencies
|
|
check_dependencies
|
|
|
|
# Check if teams directory exists
|
|
if [ ! -d "$teams_dir" ]; then
|
|
echo -e "${RED}Error: Teams directory not found: $teams_dir${NC}"
|
|
echo "Please provide a valid directory path"
|
|
exit 1
|
|
fi
|
|
|
|
# Process all YAML files in teams directory
|
|
echo -e "${BLUE}Finding team files...${NC}"
|
|
|
|
# Use nullglob to handle case where no files match the pattern
|
|
shopt -s nullglob
|
|
local team_files=("$teams_dir"/*.yml)
|
|
shopt -u nullglob
|
|
|
|
if [ ${#team_files[@]} -eq 0 ]; then
|
|
echo -e "${RED}Error: No YAML files found in teams directory${NC}"
|
|
exit 1
|
|
fi
|
|
|
|
echo -e "${BLUE}Found ${#team_files[@]} team files${NC}"
|
|
echo
|
|
|
|
# PASS 1: Process each team file (add keys to team files)
|
|
echo -e "${GREEN}=== PASS 1: UPDATING TEAM FILES ===${NC}"
|
|
for team_file in "${team_files[@]}"; do
|
|
if [ -f "$team_file" ]; then
|
|
process_team_file "$team_file"
|
|
fi
|
|
done
|
|
|
|
# PASS 2: Clean up software files (remove keys from software files)
|
|
if [ ${#PROCESSED_SOFTWARE_FILES[@]} -gt 0 ]; then
|
|
cleanup_software_files
|
|
fi
|
|
|
|
# PASS 3: Restore emoji characters that may have been converted to Unicode escape sequences
|
|
restore_emojis_in_team_files
|
|
|
|
# Summary
|
|
echo -e "${GREEN}=== PROCESSING COMPLETE ===${NC}"
|
|
echo -e "${GREEN}Teams processed: $PROCESSED_TEAMS${NC}"
|
|
echo -e "${GREEN}Packages processed: $PROCESSED_PACKAGES${NC}"
|
|
if [ $ERRORS -gt 0 ]; then
|
|
echo -e "${RED}Errors encountered: $ERRORS${NC}"
|
|
echo -e "${YELLOW}Check the output above for details${NC}"
|
|
else
|
|
echo -e "${GREEN}✓ All files processed successfully!${NC}"
|
|
fi
|
|
|
|
}
|
|
|
|
# Run main function with all script arguments
|
|
main "$@"
|