Files
fleet/tools/gitops-migrate/migrate.sh
T
Noah TalermanandRachael Shaw 62c1033868 tools/ directory: Rename "queries" => "reports" and "teams" => "fleets" (#43575)
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>
2026-05-07 10:42:39 -05:00

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 "$@"