name: josh-sync description: Bidirectional monorepo ↔ subrepo sync via josh-proxy inputs: config: description: "Path to .josh-sync.yml" default: ".josh-sync.yml" direction: description: "forward, reverse, or both" default: "both" target: description: "Sync only this target (default: all)" required: false branch: description: "Sync only this branch (default: all)" required: false debug: description: "Enable debug output" default: "false" runs: using: composite steps: - name: Setup josh-sync shell: bash run: | JOSH_DIR="$(mktemp -d)" cp -r "${{ github.action_path }}/bin" "${{ github.action_path }}/lib" "${JOSH_DIR}/" chmod +x "${JOSH_DIR}/bin/josh-sync" echo "${JOSH_DIR}/bin" >> "$GITHUB_PATH" echo "JOSH_SYNC_ROOT=${JOSH_DIR}" >> "$GITHUB_ENV" - name: Check dependencies shell: bash run: | for cmd in git curl jq yq; do command -v "$cmd" &>/dev/null || { echo "::error::Missing required tool: $cmd"; exit 1; } done - name: Loop guard (forward) id: guard if: inputs.direction == 'forward' || inputs.direction == 'both' shell: bash run: | TRAILER=$(yq '.bot.trailer' "${{ inputs.config }}" 2>/dev/null || echo "Josh-Sync-Origin") if git log -1 --format=%B | grep -q "^${TRAILER}:"; then echo "skip=true" >> "$GITHUB_OUTPUT" echo "::notice::Skipping sync — HEAD commit has sync trailer (loop prevention)" else echo "skip=false" >> "$GITHUB_OUTPUT" fi - name: Sync if: steps.guard.outputs.skip != 'true' shell: bash env: JOSH_SYNC_DEBUG: ${{ inputs.debug == 'true' && '1' || '0' }} run: | ARGS="--config ${{ inputs.config }}" [[ "${{ inputs.direction }}" == "forward" ]] && ARGS+=" --forward" [[ "${{ inputs.direction }}" == "reverse" ]] && ARGS+=" --reverse" [[ -n "${{ inputs.target }}" ]] && ARGS+=" --target ${{ inputs.target }}" [[ -n "${{ inputs.branch }}" ]] && ARGS+=" --branch ${{ inputs.branch }}" josh-sync sync $ARGS