204 lines
4.1 KiB
Bash
Executable File
204 lines
4.1 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
# monorepo-init: Auto-detect subprojects and generate .monorepo.json
|
|
#
|
|
# Usage:
|
|
# bin/monorepo-init # Output JSON to stdout
|
|
# bin/monorepo-init --dry-run # Same as above
|
|
# bin/monorepo-init --write # Write to .monorepo.json
|
|
|
|
show_usage() {
|
|
cat << EOF
|
|
Usage: monorepo-init [OPTIONS]
|
|
|
|
Auto-detect subprojects and generate .monorepo.json
|
|
|
|
OPTIONS:
|
|
--dry-run Output JSON to stdout (default)
|
|
--write Write to .monorepo.json
|
|
-h, --help Show this help
|
|
|
|
DETECTION:
|
|
Scans for package manager artifacts:
|
|
- package.json (Node)
|
|
- Gemfile (Ruby)
|
|
- go.mod (Go)
|
|
- pyproject.toml, setup.py, requirements.txt (Python)
|
|
- Cargo.toml (Rust)
|
|
- build.gradle, pom.xml (Java)
|
|
|
|
EXAMPLES:
|
|
bin/monorepo-init --dry-run
|
|
bin/monorepo-init | jq .
|
|
bin/monorepo-init --write
|
|
EOF
|
|
}
|
|
|
|
# Parse arguments
|
|
MODE="dry-run"
|
|
while [[ $# -gt 0 ]]; do
|
|
case $1 in
|
|
--write)
|
|
MODE="write"
|
|
shift
|
|
;;
|
|
--dry-run)
|
|
MODE="dry-run"
|
|
shift
|
|
;;
|
|
-h | --help)
|
|
show_usage
|
|
exit 0
|
|
;;
|
|
*)
|
|
echo "Unknown option: $1" >&2
|
|
show_usage >&2
|
|
exit 1
|
|
;;
|
|
esac
|
|
done
|
|
|
|
# Find repo root
|
|
find_repo_root() {
|
|
git rev-parse --show-toplevel 2> /dev/null || {
|
|
echo "Error: Not in a git repository" >&2
|
|
exit 1
|
|
}
|
|
}
|
|
|
|
# Detect subproject type from artifacts
|
|
detect_type() {
|
|
local dir="$1"
|
|
|
|
if [[ -f "$dir/package.json" ]]; then
|
|
echo "node"
|
|
elif [[ -f "$dir/Gemfile" ]]; then
|
|
echo "ruby"
|
|
elif [[ -f "$dir/go.mod" ]]; then
|
|
echo "go"
|
|
elif [[ -f "$dir/pyproject.toml" ]] || [[ -f "$dir/setup.py" ]] || [[ -f "$dir/requirements.txt" ]]; then
|
|
echo "python"
|
|
elif [[ -f "$dir/Cargo.toml" ]]; then
|
|
echo "rust"
|
|
elif [[ -f "$dir/build.gradle" ]] || [[ -f "$dir/pom.xml" ]]; then
|
|
echo "java"
|
|
else
|
|
echo "unknown"
|
|
fi
|
|
}
|
|
|
|
# Find all artifact files
|
|
find_artifacts() {
|
|
local root="$1"
|
|
|
|
# Check if fd is available (faster)
|
|
if command -v fd &> /dev/null; then
|
|
fd -t f '(package\.json|Gemfile|go\.mod|pyproject\.toml|setup\.py|requirements\.txt|Cargo\.toml|build\.gradle|pom\.xml)$' "$root"
|
|
else
|
|
# Fallback to find
|
|
find "$root" -type f \( \
|
|
-name 'package.json' -o \
|
|
-name 'Gemfile' -o \
|
|
-name 'go.mod' -o \
|
|
-name 'pyproject.toml' -o \
|
|
-name 'setup.py' -o \
|
|
-name 'requirements.txt' -o \
|
|
-name 'Cargo.toml' -o \
|
|
-name 'build.gradle' -o \
|
|
-name 'pom.xml' \
|
|
\)
|
|
fi
|
|
}
|
|
|
|
# Generate JSON structure
|
|
generate_json() {
|
|
local root="$1"
|
|
local subprojects="$2" # newline-separated list of "id:path:type"
|
|
|
|
# Start JSON
|
|
cat << EOF
|
|
{
|
|
"root": "$root",
|
|
"subprojects": {
|
|
EOF
|
|
|
|
# Add each subproject
|
|
local first=true
|
|
while IFS=: read -r id path type; do
|
|
if [[ "$first" == true ]]; then
|
|
first=false
|
|
else
|
|
echo ","
|
|
fi
|
|
|
|
cat << EOF
|
|
"$id": {
|
|
"path": "$path",
|
|
"type": "$type"
|
|
}
|
|
EOF
|
|
done <<< "$subprojects"
|
|
|
|
# Close JSON
|
|
cat << EOF
|
|
|
|
}
|
|
}
|
|
EOF
|
|
}
|
|
|
|
# Main logic
|
|
REPO_ROOT=$(find_repo_root)
|
|
cd "$REPO_ROOT"
|
|
|
|
# Find all artifacts and group by directory
|
|
declare -A seen_dirs
|
|
SUBPROJECTS=""
|
|
|
|
while read -r artifact_path; do
|
|
dir=$(dirname "$artifact_path")
|
|
|
|
# Skip if we've seen this directory
|
|
[[ -n "${seen_dirs[$dir]:-}" ]] && continue
|
|
seen_dirs[$dir]=1
|
|
|
|
# Generate subproject ID (relative path with / → -)
|
|
rel_path="${dir#$REPO_ROOT/}"
|
|
if [[ "$rel_path" == "$REPO_ROOT" ]] || [[ "$rel_path" == "." ]]; then
|
|
id="root"
|
|
rel_path="."
|
|
else
|
|
id="${rel_path//\//-}"
|
|
fi
|
|
|
|
# Detect type
|
|
type=$(detect_type "$dir")
|
|
|
|
# Add to list
|
|
SUBPROJECTS+="$id:$rel_path:$type"$'\n'
|
|
done < <(find_artifacts "$REPO_ROOT")
|
|
|
|
# Generate JSON
|
|
JSON=$(generate_json "$REPO_ROOT" "$SUBPROJECTS")
|
|
|
|
# Output based on mode
|
|
case "$MODE" in
|
|
dry-run)
|
|
echo "$JSON"
|
|
;;
|
|
write)
|
|
if [[ -f ".monorepo.json" ]]; then
|
|
echo "Warning: .monorepo.json already exists" >&2
|
|
echo "Overwrite? (y/N) " >&2
|
|
read -r response
|
|
if [[ ! "$response" =~ ^[Yy]$ ]]; then
|
|
echo "Aborted" >&2
|
|
exit 1
|
|
fi
|
|
fi
|
|
echo "$JSON" > .monorepo.json
|
|
echo "Written to .monorepo.json" >&2
|
|
;;
|
|
esac
|