Files
dingtalk-workspace-cli/scripts/release/check-release-compatibility.sh

123 lines
3.3 KiB
Bash
Executable File

#!/bin/sh
set -eu
# Release compatibility is one decision seam over the sealed source tree.
# Trusted release tooling may orchestrate this script, but the explicitly
# selected repository remains the authority for both CLI and Schema checks.
REPO_ROOT=""
BASE_REF=""
STABLE_REF=""
CANDIDATE_REF="HEAD"
usage() {
printf '%s\n' \
"usage: $0 --repo-root <path> --base-ref <ref> --stable-ref <ref> [--candidate-ref <ref>]" >&2
}
while [ "$#" -gt 0 ]; do
case "$1" in
--repo-root)
[ "$#" -ge 2 ] || { usage; exit 2; }
REPO_ROOT="$2"
shift 2
;;
--base-ref)
[ "$#" -ge 2 ] || { usage; exit 2; }
BASE_REF="$2"
shift 2
;;
--stable-ref)
[ "$#" -ge 2 ] || { usage; exit 2; }
STABLE_REF="$2"
shift 2
;;
--candidate-ref)
[ "$#" -ge 2 ] || { usage; exit 2; }
CANDIDATE_REF="$2"
shift 2
;;
-h|--help)
usage
exit 0
;;
*)
printf 'error: unknown argument: %s\n' "$1" >&2
usage
exit 2
;;
esac
done
[ -n "$REPO_ROOT" ] && [ -n "$BASE_REF" ] && [ -n "$STABLE_REF" ] || {
usage
exit 2
}
REPO_ROOT="$(CDPATH= cd -- "$REPO_ROOT" && pwd -P)" || {
printf 'error: release source root is not available: %s\n' "$REPO_ROOT" >&2
exit 2
}
GIT_ROOT="$(git -C "$REPO_ROOT" rev-parse --show-toplevel 2>/dev/null)" || {
printf 'error: release source root is not a Git worktree: %s\n' "$REPO_ROOT" >&2
exit 2
}
[ "$GIT_ROOT" = "$REPO_ROOT" ] || {
printf 'error: release source root must be the Git worktree root: %s\n' "$REPO_ROOT" >&2
exit 2
}
CLI_CHECK="$REPO_ROOT/scripts/policy/check-authoritative-interface-baselines.sh"
SCHEMA_CHECK="$REPO_ROOT/scripts/policy/check-authoritative-schema-compatibility.sh"
for check in "$CLI_CHECK" "$SCHEMA_CHECK"; do
[ -x "$check" ] || {
printf 'error: authoritative release compatibility checker is unavailable: %s\n' "$check" >&2
exit 2
}
done
resolve_commit() {
label="$1"
ref="$2"
commit="$(git -C "$REPO_ROOT" rev-parse --verify "${ref}^{commit}" 2>/dev/null)" || {
printf 'error: release %s ref is not available in sealed source: %s\n' "$label" "$ref" >&2
return 2
}
[ -n "$commit" ] || {
printf 'error: release %s ref resolved to an empty commit: %s\n' "$label" "$ref" >&2
return 2
}
printf '%s\n' "$commit"
}
# Freeze the complete comparison tuple before either checker runs. Otherwise a
# concurrent ref update (or the first checker itself) could make Schema inspect
# different Git objects from the CLI gate even when both receive the same names.
BASE_COMMIT="$(resolve_commit base "$BASE_REF")"
STABLE_COMMIT="$(resolve_commit stable "$STABLE_REF")"
CANDIDATE_COMMIT="$(resolve_commit candidate "$CANDIDATE_REF")"
printf '==> Checking authoritative CLI compatibility\n'
if "$CLI_CHECK" \
--base-ref "$BASE_COMMIT" \
--stable-ref "$STABLE_COMMIT" \
--candidate-ref "$CANDIDATE_COMMIT"; then
:
else
status=$?
printf 'error: authoritative CLI compatibility failed\n' >&2
exit "$status"
fi
printf '==> Checking authoritative Schema compatibility\n'
if "$SCHEMA_CHECK" \
--base-ref "$BASE_COMMIT" \
--stable-ref "$STABLE_COMMIT" \
--candidate-ref "$CANDIDATE_COMMIT"; then
:
else
status=$?
printf 'error: authoritative Schema compatibility failed\n' >&2
exit "$status"
fi