#!/usr/bin/env bash # Regenerates plugin-taskwork/patches/*.patch from the fork's feature branches. # # The branches in cloudcli/ are the single source of truth (SPEC-v3 §12.0); # the .patch files are a derived artefact and are never edited by hand. # # Usage: ./scripts/export-patches.sh [base-tag] set -euo pipefail ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" BASE_TAG="${1:-v1.37.2}" SRC="$ROOT/cloudcli" OUT="$ROOT/plugin-taskwork/patches" # branch:output-file — the numeric prefix mirrors the publication order (§12.4), # not a dependency: every patch applies to the base tag on its own. BRANCHES=( "feat/resizable-sidebar:0001-feat-resizable-sidebar.patch" "feat/plugin-host-api:0002-feat-plugin-host-api.patch" "feat/plugin-sidebar-surface:0003-feat-plugin-sidebar-surface.patch" ) # -e, not -d: in a submodule checkout .git is a file, not a directory. [ -e "$SRC/.git" ] || { echo "cloudcli/ submodule is not initialised — run ./scripts/bootstrap.sh" >&2; exit 1; } [ -d "$OUT" ] || { echo "plugin-taskwork/patches/ is missing — run ./scripts/bootstrap.sh" >&2; exit 1; } git -C "$SRC" rev-parse --verify --quiet "$BASE_TAG^{commit}" >/dev/null \ || { echo "base tag $BASE_TAG not found in cloudcli/ (git fetch upstream --tags)" >&2; exit 1; } for entry in "${BRANCHES[@]}"; do branch="${entry%%:*}" file="${entry##*:}" if ! git -C "$SRC" rev-parse --verify --quiet "$branch" >/dev/null; then echo "skip $branch (branch does not exist yet)" continue fi # Independence check: a feature branch must sit directly on the base tag, # otherwise its patch would silently carry another PR's commits (§4.4). base="$(git -C "$SRC" merge-base "$BASE_TAG" "$branch")" if [ "$base" != "$(git -C "$SRC" rev-parse "$BASE_TAG^{commit}")" ]; then echo "ERROR: $branch is not based on $BASE_TAG" >&2 exit 1 fi git -C "$SRC" format-patch "$BASE_TAG..$branch" --stdout > "$OUT/$file" commits="$(git -C "$SRC" rev-list --count "$BASE_TAG..$branch")" echo "wrote patches/$file ($commits commit(s))" done