Files
2026-07-13 12:35:03 +08:00

554 lines
17 KiB
Bash
Executable File

#!/usr/bin/env bash
#
# Migrate ArcKit project files to new Document ID-based filenames
# Usage: ./migrate-filenames.sh [PROJECT_DIR] [OPTIONS]
#
# This script renames old-style filenames to the new ARC-{PID}-{TYPE}-v{VERSION}.md pattern.
# A backup is created before any changes are made.
#
# Examples:
# ./migrate-filenames.sh projects/001-payment-gateway
# ./migrate-filenames.sh projects/001-payment-gateway --dry-run
# ./migrate-filenames.sh --all # Migrate all projects
set -euo pipefail
# Enable nullglob so non-matching globs expand to nothing
shopt -s nullglob
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "$SCRIPT_DIR/common.sh"
# File mapping: old filename -> type code
declare -A FILE_MAPPING=(
# Core documents
["requirements.md"]="REQ"
["stakeholder-drivers.md"]="STKE"
["stakeholder-analysis.md"]="STKE" # Alternative name
["risk-register.md"]="RISK"
["sobc.md"]="SOBC"
["data-model.md"]="DATA"
# research-findings.md moved to research/ subdirectory (multi-instance)
["traceability-matrix.md"]="TRAC"
["analysis-report.md"]="ANAL"
# Global documents (for 000-global)
["architecture-principles.md"]="PRIN"
["principles.md"]="PRIN" # Alternative name
# Strategy & Operations
["project-plan.md"]="PLAN"
["roadmap.md"]="ROAD"
["devops-strategy.md"]="DEVOPS"
["mlops-strategy.md"]="MLOPS"
["finops-strategy.md"]="FINOPS"
["operational-readiness.md"]="OPS"
["servicenow-design.md"]="SNOW"
["backlog.md"]="BKLG"
["platform-design.md"]="PLAT"
# Procurement
["sow.md"]="SOW"
["dos-requirements.md"]="DOS"
["digital-marketplace-dos.md"]="DOS" # Alternative name
["gcloud-requirements.md"]="GCLD"
["gcloud-clarification-questions.md"]="GCLC"
["evaluation-criteria.md"]="EVAL"
# Compliance
["tcop-review.md"]="TCOP"
["tcop-assessment.md"]="TCOP" # Alternative name
["ukgov-secure-by-design.md"]="SECD"
["secure-by-design.md"]="SECD" # Alternative name
["mod-secure-by-design.md"]="SECD-MOD"
["ai-playbook-assessment.md"]="AIPB"
["atrs-record.md"]="ATRS"
["jsp-936.md"]="JSP936"
["jsp936.md"]="JSP936" # Alternative name
["dpia.md"]="DPIA"
["principles-compliance-assessment.md"]="PRIN-COMP"
["conformance-assessment.md"]="CONF"
# Reviews
["hld-review.md"]="HLDR"
["hld.md"]="HLDR" # Alternative name
["dld-review.md"]="DLDR"
["dld.md"]="DLDR" # Alternative name
# Other
["PROJECT-STORY.md"]="STORY"
)
# Subdirectory mappings for multi-instance types
declare -A SUBDIR_MAPPING=(
["decisions"]="ADR"
["diagrams"]="DIAG"
["wardley-maps"]="WARD"
["data-contracts"]="DMC"
["research"]="RSCH"
)
usage() {
cat <<EOF
Usage: $0 [PROJECT_DIR] [OPTIONS]
Migrate ArcKit project files to new Document ID-based filenames.
Options:
--all Migrate all projects in the projects/ directory
--global Migrate only the global directory (000-global)
--dry-run Show what would be changed without making changes
--no-backup Skip creating backup (not recommended)
--force Overwrite existing files if they exist
--help, -h Show this help message
Examples:
$0 projects/001-payment-gateway
$0 projects/001-payment-gateway --dry-run
$0 projects/000-global # Migrate global principles
$0 --global --dry-run # Dry run for global only
$0 --all --dry-run # Migrate all (including global)
Supported global files (000-global):
architecture-principles.md → ARC-000-PRIN-v1.0.md
principles.md → ARC-000-PRIN-v1.0.md
Legacy locations checked for principles:
.arckit/memory/architecture-principles.md
.arckit/memory/principles.md
EOF
exit 1
}
# Parse arguments
PROJECT_DIR=""
MIGRATE_ALL=false
MIGRATE_GLOBAL=false
DRY_RUN=false
NO_BACKUP=false
FORCE=false
while [[ $# -gt 0 ]]; do
case $1 in
--all)
MIGRATE_ALL=true
shift
;;
--global)
MIGRATE_GLOBAL=true
shift
;;
--dry-run)
DRY_RUN=true
shift
;;
--no-backup)
NO_BACKUP=true
shift
;;
--force)
FORCE=true
shift
;;
--help|-h)
usage
;;
-*)
log_error "Unknown option: $1"
usage
;;
*)
PROJECT_DIR="$1"
shift
;;
esac
done
# Find repository root
REPO_ROOT="$(find_repo_root)"
# Validate arguments
if [[ "$MIGRATE_ALL" == "false" && "$MIGRATE_GLOBAL" == "false" && -z "$PROJECT_DIR" ]]; then
log_error "PROJECT_DIR required (or use --all or --global)"
usage
fi
# Get project number from directory name
get_project_id() {
local dir="$1"
local basename=$(basename "$dir")
# Handle 000-global specially
if [[ "$basename" == "000-global" ]]; then
echo "000"
return 0
fi
if [[ "$basename" =~ ^([0-9]{3})- ]]; then
echo "${BASH_REMATCH[1]}"
else
log_error "Cannot extract project ID from: $basename"
return 1
fi
}
# Migrate a single file
migrate_file() {
local old_path="$1"
local new_path="$2"
local backup_dir="$3"
if [[ ! -f "$old_path" ]]; then
return 0
fi
if [[ -f "$new_path" && "$FORCE" == "false" ]]; then
log_warning "Target exists, skipping: $new_path"
return 0
fi
if [[ "$DRY_RUN" == "true" ]]; then
log_info "[DRY-RUN] Would rename: $(basename "$old_path")$(basename "$new_path")"
else
# Create backup
if [[ "$NO_BACKUP" == "false" ]]; then
mkdir -p "$backup_dir"
cp "$old_path" "$backup_dir/"
fi
# Rename file
mv "$old_path" "$new_path"
log_success "Renamed: $(basename "$old_path")$(basename "$new_path")"
fi
}
# Migrate files in a subdirectory (ADR, diagrams, etc.)
migrate_subdir() {
local subdir="$1"
local project_id="$2"
local type_code="$3"
local backup_dir="$4"
if [[ ! -d "$subdir" ]]; then
return 0
fi
local count=0
# Find existing files that don't match new pattern
for file in "$subdir"/*.md; do
if [[ ! -f "$file" ]]; then
continue
fi
local basename=$(basename "$file")
# Skip if already in new format
if [[ "$basename" =~ ^ARC-[0-9]{3}-${type_code}-[0-9]{3}-v[0-9]+\.[0-9]+\.md$ ]]; then
continue
fi
# Determine sequence number
count=$((count + 1))
local seq_num=$(printf "%03d" $count)
local new_name="ARC-${project_id}-${type_code}-${seq_num}-v1.0.md"
local new_path="$subdir/$new_name"
migrate_file "$file" "$new_path" "$backup_dir"
done
}
# Migrate a single project
migrate_project() {
local project_dir="$1"
# Create directory if it doesn't exist (especially for 000-global)
if [[ ! -d "$project_dir" ]]; then
local basename=$(basename "$project_dir")
if [[ "$basename" == "000-global" ]]; then
log_info "Creating global directory: $project_dir"
if [[ "$DRY_RUN" == "false" ]]; then
mkdir -p "$project_dir"
else
log_info "[DRY-RUN] Would create directory: $project_dir"
fi
else
log_error "Project directory not found: $project_dir"
return 1
fi
fi
local project_id
project_id=$(get_project_id "$project_dir") || return 1
log_info "Migrating project: $(basename "$project_dir") (ID: $project_id)"
local backup_dir="$project_dir/.backup/$(date +%Y%m%d_%H%M%S)"
# Special handling for 000-global: check legacy locations for principles
if [[ "$project_id" == "000" ]]; then
local legacy_locations=(
"$REPO_ROOT/.arckit/memory/architecture-principles.md"
"$REPO_ROOT/.arckit/memory/principles.md"
)
for legacy_path in "${legacy_locations[@]}"; do
if [[ -f "$legacy_path" ]]; then
local new_name="ARC-000-PRIN-v1.0.md"
local new_path="$project_dir/$new_name"
if [[ -f "$new_path" && "$FORCE" == "false" ]]; then
log_warning "Principles already exist at: $new_path (use --force to overwrite)"
else
log_info "Found principles at legacy location: $legacy_path"
migrate_file "$legacy_path" "$new_path" "$backup_dir"
fi
break
fi
done
fi
# Create subdirectories for new structure if they don't exist (skip for 000-global)
if [[ "$DRY_RUN" == "false" && "$project_id" != "000" ]]; then
mkdir -p "$project_dir/decisions"
mkdir -p "$project_dir/diagrams"
mkdir -p "$project_dir/wardley-maps"
mkdir -p "$project_dir/data-contracts"
mkdir -p "$project_dir/reviews"
mkdir -p "$project_dir/research"
fi
# Migrate root-level files
for old_name in "${!FILE_MAPPING[@]}"; do
local type_code="${FILE_MAPPING[$old_name]}"
local old_path="$project_dir/$old_name"
if [[ -f "$old_path" ]]; then
# Handle date-suffixed files (e.g., principles-compliance-assessment-2025-01-15.md)
local new_name="ARC-${project_id}-${type_code}-v1.0.md"
# Determine subdirectory for reviews
if [[ "$type_code" == "HLDR" || "$type_code" == "DLDR" ]]; then
local new_path="$project_dir/reviews/$new_name"
else
local new_path="$project_dir/$new_name"
fi
migrate_file "$old_path" "$new_path" "$backup_dir"
fi
done
# Also check procurement/ subdirectory for old-style files
if [[ -d "$project_dir/procurement" ]]; then
for old_name in "${!FILE_MAPPING[@]}"; do
local type_code="${FILE_MAPPING[$old_name]}"
local old_path="$project_dir/procurement/$old_name"
if [[ -f "$old_path" ]]; then
local new_name="ARC-${project_id}-${type_code}-v1.0.md"
local new_path="$project_dir/$new_name"
migrate_file "$old_path" "$new_path" "$backup_dir"
fi
done
fi
# Migrate date-suffixed compliance files (e.g., principles-compliance-assessment-YYYY-MM-DD.md)
for file in "$project_dir"/principles-compliance-assessment-*.md; do
if [[ -f "$file" ]]; then
local new_name="ARC-${project_id}-PRIN-COMP-v1.0.md"
migrate_file "$file" "$project_dir/$new_name" "$backup_dir"
break # Only migrate the first one
fi
done
# Migrate date-suffixed service assessment files
for file in "$project_dir"/service-assessment-*-prep.md; do
if [[ -f "$file" ]]; then
local new_name="ARC-${project_id}-SVCASS-v1.0.md"
migrate_file "$file" "$project_dir/$new_name" "$backup_dir"
break
fi
done
# Migrate subdirectories
for subdir_name in "${!SUBDIR_MAPPING[@]}"; do
local type_code="${SUBDIR_MAPPING[$subdir_name]}"
migrate_subdir "$project_dir/$subdir_name" "$project_id" "$type_code" "$backup_dir"
done
# Also check for old wardley-maps location at root level
local ward_count=0
if [[ -d "$project_dir/wardley-maps" ]]; then
ward_count=$(find "$project_dir/wardley-maps" -maxdepth 1 -name "ARC-*.md" 2>/dev/null | wc -l)
fi
for file in "$project_dir"/*-wardley.md "$project_dir"/*-map.md; do
if [[ -f "$file" ]]; then
ward_count=$((ward_count + 1))
local seq_num=$(printf "%03d" $ward_count)
local new_name="ARC-${project_id}-WARD-${seq_num}-v1.0.md"
if [[ "$DRY_RUN" == "false" ]]; then
mkdir -p "$project_dir/wardley-maps"
fi
migrate_file "$file" "$project_dir/wardley-maps/$new_name" "$backup_dir"
fi
done
# Check for old diagram files at root
local diag_count=0
if [[ -d "$project_dir/diagrams" ]]; then
diag_count=$(find "$project_dir/diagrams" -maxdepth 1 -name "ARC-*.md" 2>/dev/null | wc -l)
fi
for file in "$project_dir"/*-diagram.md "$project_dir"/diagram-*.md; do
if [[ -f "$file" ]]; then
diag_count=$((diag_count + 1))
local seq_num=$(printf "%03d" $diag_count)
local new_name="ARC-${project_id}-DIAG-${seq_num}-v1.0.md"
if [[ "$DRY_RUN" == "false" ]]; then
mkdir -p "$project_dir/diagrams"
fi
migrate_file "$file" "$project_dir/diagrams/$new_name" "$backup_dir"
fi
done
# Check for ADR files at root level (should be in decisions/)
local adr_count=0
if [[ -d "$project_dir/decisions" ]]; then
adr_count=$(find "$project_dir/decisions" -maxdepth 1 -name "ARC-*.md" 2>/dev/null | wc -l)
fi
for file in "$project_dir"/adr-*.md "$project_dir"/ADR-*.md; do
if [[ -f "$file" ]]; then
adr_count=$((adr_count + 1))
local seq_num=$(printf "%03d" $adr_count)
local new_name="ARC-${project_id}-ADR-${seq_num}-v1.0.md"
if [[ "$DRY_RUN" == "false" ]]; then
mkdir -p "$project_dir/decisions"
fi
migrate_file "$file" "$project_dir/decisions/$new_name" "$backup_dir"
fi
done
# Check for research files at root level (should be in research/)
local rsch_count=0
if [[ -d "$project_dir/research" ]]; then
rsch_count=$(find "$project_dir/research" -maxdepth 1 -name "ARC-*.md" 2>/dev/null | wc -l)
fi
for file in "$project_dir"/research-*.md "$project_dir"/research.md; do
if [[ -f "$file" ]]; then
rsch_count=$((rsch_count + 1))
local seq_num=$(printf "%03d" $rsch_count)
local new_name="ARC-${project_id}-RSCH-${seq_num}-v1.0.md"
if [[ "$DRY_RUN" == "false" ]]; then
mkdir -p "$project_dir/research"
fi
migrate_file "$file" "$project_dir/research/$new_name" "$backup_dir"
fi
done
# Check for version-suffixed traceability files (e.g., traceability-matrix-v4.md)
for file in "$project_dir"/traceability-matrix-v*.md; do
if [[ -f "$file" ]]; then
local new_name="ARC-${project_id}-TRAC-v1.0.md"
local new_path="$project_dir/$new_name"
if [[ ! -f "$new_path" || "$FORCE" == "true" ]]; then
migrate_file "$file" "$new_path" "$backup_dir"
fi
fi
done
if [[ "$DRY_RUN" == "true" ]]; then
log_info "[DRY-RUN] No changes made"
else
if [[ -d "$backup_dir" && -n $(ls -A "$backup_dir" 2>/dev/null) ]]; then
log_success "Backup created at: $backup_dir"
fi
fi
log_success "Migration complete for: $(basename "$project_dir")"
}
# Migrate global principles from legacy locations
migrate_global_principles() {
local global_dir="$REPO_ROOT/projects/000-global"
local backup_dir="$global_dir/.backup/$(date +%Y%m%d_%H%M%S)"
# Check legacy locations for principles
local legacy_locations=(
"$REPO_ROOT/.arckit/memory/architecture-principles.md"
"$REPO_ROOT/.arckit/memory/principles.md"
)
for legacy_path in "${legacy_locations[@]}"; do
if [[ -f "$legacy_path" ]]; then
local new_name="ARC-000-PRIN-v1.0.md"
local new_path="$global_dir/$new_name"
if [[ -f "$new_path" && "$FORCE" == "false" ]]; then
log_info "Principles already exist at: $new_path"
return 0
fi
log_info "Found principles at legacy location: $legacy_path"
if [[ "$DRY_RUN" == "false" ]]; then
mkdir -p "$global_dir"
fi
migrate_file "$legacy_path" "$new_path" "$backup_dir"
return 0
fi
done
}
# Main execution
# Always try to migrate global principles first
migrate_global_principles
if [[ "$MIGRATE_GLOBAL" == "true" ]]; then
# Migrate only the global directory (will be created if it doesn't exist)
GLOBAL_DIR="$REPO_ROOT/projects/000-global"
migrate_project "$GLOBAL_DIR"
elif [[ "$MIGRATE_ALL" == "true" ]]; then
PROJECTS_DIR="$REPO_ROOT/projects"
if [[ ! -d "$PROJECTS_DIR" ]]; then
log_error "No projects directory found at: $PROJECTS_DIR"
exit 1
fi
project_count=0
for project_dir in "$PROJECTS_DIR"/*/; do
if [[ -d "$project_dir" ]]; then
migrate_project "$project_dir"
project_count=$((project_count + 1))
fi
done
log_success "Migrated $project_count projects"
else
# Handle relative or absolute path
if [[ "$PROJECT_DIR" != /* ]]; then
PROJECT_DIR="$REPO_ROOT/$PROJECT_DIR"
fi
migrate_project "$PROJECT_DIR"
fi
# Summary
echo ""
if [[ "$DRY_RUN" == "true" ]]; then
log_info "This was a dry run. Run without --dry-run to apply changes."
else
log_success "Migration complete!"
echo ""
echo "Next steps:"
echo " 1. Review the changes and backups"
echo " 2. Update any references to old filenames in your documentation"
echo " 3. Commit the changes to version control"
fi