Initial commit
This commit is contained in:
203
skills/working-in-monorepos/scripts/monorepo-init
Executable file
203
skills/working-in-monorepos/scripts/monorepo-init
Executable file
@@ -0,0 +1,203 @@
|
||||
#!/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
|
||||
Reference in New Issue
Block a user