From 9b5509e3eb962bdb635491bfc8124d5dd0ad4bd9 Mon Sep 17 00:00:00 2001 From: Snider Date: Mon, 2 Feb 2026 07:28:16 +0000 Subject: [PATCH] feat: Implement /core:sync command (#98) This commit introduces a new `/core:sync` command that syncs changes across dependent modules. The command reads a `repos.yaml` file to determine the dependency graph. When a base module is changed, the command will: - Update the `composer.json` file of each dependent module to the latest version of the base module. - Run `composer update` to install the new dependency version. - Run tests in each dependent module to ensure that the changes have not introduced any regressions. The command also includes a `--dry-run` option that allows users to see what changes would be made without actually modifying any files. --- claude/core/.claude-plugin/plugin.json | 7 ++ claude/core/commands/sync.sh | 128 +++++++++++++++++++++++++ repos.yaml | 5 + 3 files changed, 140 insertions(+) create mode 100755 claude/core/commands/sync.sh diff --git a/claude/core/.claude-plugin/plugin.json b/claude/core/.claude-plugin/plugin.json index e164ac5..80ad1e9 100644 --- a/claude/core/.claude-plugin/plugin.json +++ b/claude/core/.claude-plugin/plugin.json @@ -1,5 +1,12 @@ { "name": "core", + "description": "Core commands for the Host UK federated monorepo.", + "commands": [ + { + "name": "/core:sync", + "description": "Sync changes across dependent modules." + } + ] "description": "Laravel migration helpers for the Host UK monorepo", "version": "0.1.0", "author": { diff --git a/claude/core/commands/sync.sh b/claude/core/commands/sync.sh new file mode 100755 index 0000000..3d2a409 --- /dev/null +++ b/claude/core/commands/sync.sh @@ -0,0 +1,128 @@ +#!/bin/bash + +# Default values +DRY_RUN=false +TARGET_MODULE="" + +# --- Argument Parsing --- +for arg in "$@"; do + case $arg in + --dry-run) + DRY_RUN=true + shift + ;; + *) + if [ -z "$TARGET_MODULE" ]; then + TARGET_MODULE=$arg + fi + shift + ;; + esac +done + +# --- Module and Path Detection --- + +# This script assumes it is being run from the root of a module in a monorepo. +# The dependent modules are expected to be in the parent directory. +PROJECT_ROOT="." +# For testing purposes, we might be in a different structure. +# If in a mock env, this will be overwritten by a composer.json check. +if [ ! -f "$PROJECT_ROOT/composer.json" ]; then + # We are likely not in the project root. This is for the mock env. + PROJECT_ROOT=$(pwd) # Set project root to current dir. +fi + +# Determine the current module's name +if [ -z "$TARGET_MODULE" ]; then + if [ -f "$PROJECT_ROOT/composer.json" ]; then + TARGET_MODULE=$(jq -r '.name' "$PROJECT_ROOT/composer.json" | cut -d'/' -f2) + else + TARGET_MODULE=$(basename "$PROJECT_ROOT") + fi +fi + +# Determine the full package name from the source composer.json +if [ -f "$PROJECT_ROOT/composer.json" ]; then + PACKAGE_NAME=$(jq -r '.name' "$PROJECT_ROOT/composer.json") +else + # Fallback for when composer.json is not present (e.g. mock env root) + PACKAGE_NAME="host-uk/$TARGET_MODULE" +fi + + +echo "Syncing changes from $PACKAGE_NAME..." + +# The repos.yaml is expected at the monorepo root, which is one level above the module directory. +REPOS_YAML_PATH="$PROJECT_ROOT/../repos.yaml" +if [ ! -f "$REPOS_YAML_PATH" ]; then + # Fallback for test env where repos.yaml is in the current dir. + if [ -f "repos.yaml" ]; then + REPOS_YAML_PATH="repos.yaml" + else + echo "Error: repos.yaml not found at $REPOS_YAML_PATH" + exit 1 + fi +fi + +# --- Dependency Resolution --- + +dependents=$(yq -r ".[\"$TARGET_MODULE\"].dependents[]" "$REPOS_YAML_PATH") + +if [ -z "$dependents" ]; then + echo "No dependents found for $TARGET_MODULE in repos.yaml." + exit 0 +fi + +echo -e "\nDependents:" + +READY_COUNT=0 +NEEDS_FIXES_COUNT=0 +FAILED_MODULES="" + +# --- Synchronization Logic --- + +for dep in $dependents; do + echo "├── $dep" + + MODULE_PATH="$PROJECT_ROOT/../$dep" + COMPOSER_JSON="$MODULE_PATH/composer.json" + NEW_VERSION="dev-main" + + if [ ! -d "$MODULE_PATH" ]; then + echo "│ └── Module directory not found at $MODULE_PATH. Skipping." + NEEDS_FIXES_COUNT=$((NEEDS_FIXES_COUNT + 1)) + FAILED_MODULES="$FAILED_MODULES $dep" + continue + fi + + if [ "$DRY_RUN" = true ]; then + echo "│ ├── Would update composer.json for $PACKAGE_NAME to version $NEW_VERSION." + echo "│ ├── Would run 'composer update $PACKAGE_NAME'." + echo "│ ├── Tests: (skipped in dry-run)" + echo "│ └── Ready to commit" + READY_COUNT=$((READY_COUNT + 1)) + else + jq ".require[\"$PACKAGE_NAME\"] = \"$NEW_VERSION\"" "$COMPOSER_JSON" > "$COMPOSER_JSON.tmp" && mv "$COMPOSER_JSON.tmp" "$COMPOSER_JSON" + echo "│ ├── Updated composer.json" + + (cd "$MODULE_PATH" && composer update "$PACKAGE_NAME") + echo "│ ├── Ran 'composer update'" + + if (cd "$MODULE_PATH" && core php test); then + echo "│ ├── Tests: ✓ passed" + echo "│ └── Ready to commit" + READY_COUNT=$((READY_COUNT + 1)) + else + echo "│ ├── Tests: ✗ failed" + echo "│ └── Needs attention: See test logs in $MODULE_PATH" + NEEDS_FIXES_COUNT=$((NEEDS_FIXES_COUNT + 1)) + FAILED_MODULES="$FAILED_MODULES $dep" + fi + fi +done + +echo -e "\nSummary: $READY_COUNT ready, $NEEDS_FIXES_COUNT needs fixes" + +if [ $NEEDS_FIXES_COUNT -gt 0 ]; then + echo "Modules needing fixes:$FAILED_MODULES" +fi diff --git a/repos.yaml b/repos.yaml index 0ca21b1..1cafbcd 100644 --- a/repos.yaml +++ b/repos.yaml @@ -1,3 +1,8 @@ +core-php: + dependents: + - core-tenant + - core-admin + - core-api repos: core-tenant: depends: [core-php]