TenantAtlas/scripts/resolve-feature-base
Ahmed Darrazi 8144a3be78
Some checks failed
PR Fast Feedback / fast-feedback (pull_request) Failing after 7m2s
feat: implement spec 439 branch and evidence truth
2026-07-11 21:13:42 +02:00

486 lines
16 KiB
Bash
Executable File

#!/usr/bin/env bash
set -euo pipefail
format="human"
cli_family=""
work_classification=""
pull_request_target=""
spec_path=""
branch_name=""
repo_scope=""
explicit_ref=""
usage() {
cat <<'EOF'
Usage: scripts/resolve-feature-base [options]
Options:
--branch-family=platform|website|repository-wide
--work-classification=platform|website|cross-stream|promotion|repository-governance
--pull-request-target=<platform-dev|website-dev|dev>
--spec=<path-to-spec.md>
--branch-name=<feature-branch>
--repo-scope=platform|website|repository
--ref=<family-matching-git-ref>
--format=human|kv
EOF
}
fail_resolution() {
local reason="$1"
shift
if [[ "${format}" == "human" ]]; then
echo "Feature base resolution failed." >&2
fi
echo "status=failed" >&2
echo "reason=${reason}" >&2
for detail in "$@"; do
echo "${detail}" >&2
done
echo "guidance=Declare a consistent branch family, work classification, PR target, active spec, and family-matching ref." >&2
echo "diff_ran=false" >&2
exit 2
}
while [[ $# -gt 0 ]]; do
case "$1" in
--branch-family=*)
cli_family="${1#*=}"
shift
;;
--branch-family)
[[ $# -ge 2 ]] || fail_resolution "missing-option-value" "option=--branch-family"
cli_family="$2"
shift 2
;;
--work-classification=*)
work_classification="${1#*=}"
shift
;;
--work-classification)
[[ $# -ge 2 ]] || fail_resolution "missing-option-value" "option=--work-classification"
work_classification="$2"
shift 2
;;
--pull-request-target=*)
pull_request_target="${1#*=}"
shift
;;
--pull-request-target)
[[ $# -ge 2 ]] || fail_resolution "missing-option-value" "option=--pull-request-target"
pull_request_target="$2"
shift 2
;;
--spec=*)
spec_path="${1#*=}"
shift
;;
--spec)
[[ $# -ge 2 ]] || fail_resolution "missing-option-value" "option=--spec"
spec_path="$2"
shift 2
;;
--branch-name=*)
branch_name="${1#*=}"
shift
;;
--branch-name)
[[ $# -ge 2 ]] || fail_resolution "missing-option-value" "option=--branch-name"
branch_name="$2"
shift 2
;;
--repo-scope=*)
repo_scope="${1#*=}"
shift
;;
--repo-scope)
[[ $# -ge 2 ]] || fail_resolution "missing-option-value" "option=--repo-scope"
repo_scope="$2"
shift 2
;;
--ref=*)
explicit_ref="${1#*=}"
shift
;;
--ref)
[[ $# -ge 2 ]] || fail_resolution "missing-option-value" "option=--ref"
explicit_ref="$2"
shift 2
;;
--format=*)
format="${1#*=}"
shift
;;
--format)
[[ $# -ge 2 ]] || fail_resolution "missing-option-value" "option=--format"
format="$2"
shift 2
;;
--help|-h)
usage
exit 0
;;
*)
fail_resolution "unknown-option" "option=$1"
;;
esac
done
if [[ "${format}" != "human" && "${format}" != "kv" ]]; then
fail_resolution "invalid-format" "format=${format}"
fi
normalize_family() {
case "$1" in
platform)
echo "platform"
;;
website)
echo "website"
;;
repository-wide)
echo "repository-wide"
;;
*)
return 1
;;
esac
}
family_for_branch() {
case "$1" in
platform-dev|origin/platform-dev|refs/heads/platform-dev|refs/remotes/origin/platform-dev)
echo "platform"
;;
website-dev|origin/website-dev|refs/heads/website-dev|refs/remotes/origin/website-dev)
echo "website"
;;
dev|origin/dev|refs/heads/dev|refs/remotes/origin/dev)
echo "repository-wide"
;;
*)
return 1
;;
esac
}
expected_branch_for_family() {
case "$1" in
platform)
echo "platform-dev"
;;
website)
echo "website-dev"
;;
repository-wide)
echo "dev"
;;
esac
}
trim_value() {
local value="$1"
value="${value#"${value%%[![:space:]]*}"}"
value="${value%"${value##*[![:space:]]}"}"
value="${value//\`/}"
printf '%s\n' "${value}"
}
metadata_value() {
local file="$1"
local label="$2"
local line
line="$(awk -v prefix="**${label}**:" 'index($0, prefix) == 1 { print substr($0, length(prefix) + 1); exit }' "${file}")"
trim_value "${line}"
}
repository_root="$(git rev-parse --show-toplevel 2>/dev/null || true)"
if [[ -z "${repository_root}" ]]; then
fail_resolution "not-a-git-repository"
fi
if [[ -z "${branch_name}" ]]; then
branch_name="$(git symbolic-ref --quiet --short HEAD 2>/dev/null || true)"
fi
environment_family="${TENANTATLAS_BRANCH_FAMILY:-}"
if [[ -n "${cli_family}" && -n "${environment_family}" && "${cli_family}" != "${environment_family}" ]]; then
fail_resolution "branch-family-conflict" "cli_family=${cli_family}" "workflow_family=${environment_family}"
fi
explicit_family_raw="${cli_family:-${environment_family}}"
explicit_family=""
if [[ -n "${explicit_family_raw}" ]]; then
explicit_family="$(normalize_family "${explicit_family_raw}" || true)"
if [[ -z "${explicit_family}" ]]; then
fail_resolution "invalid-branch-family" "explicit=${explicit_family_raw}"
fi
fi
environment_classification="${TENANTATLAS_WORK_CLASSIFICATION:-}"
if [[ -n "${work_classification}" && -n "${environment_classification}" && "${work_classification}" != "${environment_classification}" ]]; then
fail_resolution "work-classification-conflict" "cli_classification=${work_classification}" "workflow_classification=${environment_classification}"
fi
work_classification="${work_classification:-${environment_classification}}"
case "${work_classification}" in
""|platform|website|cross-stream|promotion|repository-governance)
;;
*)
fail_resolution "invalid-work-classification" "work_classification=${work_classification}"
;;
esac
environment_ref="${TENANTATLAS_BASE_REF:-}"
if [[ -n "${explicit_ref}" && -n "${environment_ref}" && "${explicit_ref}" != "${environment_ref}" ]]; then
fail_resolution "explicit-ref-conflict" "cli_ref=${explicit_ref}" "workflow_ref=${environment_ref}"
fi
explicit_ref="${explicit_ref:-${environment_ref}}"
resolved_target=""
target_family=""
for target_signal in \
"${pull_request_target}" \
"${TENANTATLAS_PR_TARGET:-}" \
"${GITHUB_BASE_REF:-}" \
"${GITEA_BASE_REF:-}"; do
[[ -z "${target_signal}" ]] && continue
signal_family="$(family_for_branch "${target_signal}" || true)"
if [[ -z "${signal_family}" ]]; then
fail_resolution "unknown-pull-request-target" "pull_request_target=${target_signal}"
fi
if [[ -z "${resolved_target}" ]]; then
resolved_target="${target_signal}"
target_family="${signal_family}"
elif [[ "${target_family}" != "${signal_family}" ]]; then
fail_resolution "pull-request-target-conflict" "pull_request_targets=${resolved_target},${target_signal}"
fi
done
explicit_spec_requested=false
if [[ -n "${spec_path}" ]]; then
explicit_spec_requested=true
elif [[ -n "${TENANTATLAS_ACTIVE_SPEC:-}" ]]; then
spec_path="${TENANTATLAS_ACTIVE_SPEC}"
explicit_spec_requested=true
elif [[ "${branch_name}" == feat/* ]]; then
candidate_spec="${repository_root}/specs/${branch_name#feat/}/spec.md"
if [[ -f "${candidate_spec}" ]]; then
spec_path="${candidate_spec}"
fi
fi
if [[ -n "${spec_path}" && "${spec_path}" != /* ]]; then
spec_path="${repository_root}/${spec_path}"
fi
if [[ "${explicit_spec_requested}" == true && ! -f "${spec_path}" ]]; then
fail_resolution "spec-not-readable" "spec_path=${spec_path}"
fi
spec_family=""
spec_classification=""
spec_declared_target=""
if [[ -n "${spec_path}" && -f "${spec_path}" ]]; then
feature_base="$(metadata_value "${spec_path}" "Feature Base")"
merge_target="$(metadata_value "${spec_path}" "Normal Merge Target")"
spec_declared_target="${merge_target}"
development_stream="$(metadata_value "${spec_path}" "Development Stream")"
for metadata_branch in "${feature_base}" "${merge_target}"; do
[[ -z "${metadata_branch}" ]] && continue
metadata_family="$(family_for_branch "${metadata_branch}" || true)"
if [[ -z "${metadata_family}" ]]; then
fail_resolution "unknown-spec-branch" "spec_branch=${metadata_branch}" "spec_path=${spec_path}"
fi
if [[ -z "${spec_family}" ]]; then
spec_family="${metadata_family}"
elif [[ "${spec_family}" != "${metadata_family}" ]]; then
fail_resolution "spec-metadata-conflict" "feature_base=${feature_base}" "normal_merge_target=${merge_target}" "spec_path=${spec_path}"
fi
done
case "${development_stream}" in
platform*) spec_classification="platform" ;;
website*) spec_classification="website" ;;
cross-stream*) spec_classification="cross-stream" ;;
promotion*) spec_classification="promotion" ;;
repository-governance*) spec_classification="repository-governance" ;;
esac
fi
if [[ -n "${work_classification}" && -n "${spec_classification}" && "${work_classification}" != "${spec_classification}" ]]; then
fail_resolution "work-classification-conflict" "explicit_classification=${work_classification}" "spec_classification=${spec_classification}"
fi
work_classification="${work_classification:-${spec_classification}}"
branch_hint=""
lower_branch="$(printf '%s' "${branch_name}" | tr '[:upper:]' '[:lower:]')"
if [[ "${lower_branch}" == *platform* && "${lower_branch}" == *website* ]]; then
branch_hint="ambiguous"
elif [[ "${lower_branch}" == *platform* ]]; then
branch_hint="platform"
elif [[ "${lower_branch}" == *website* ]]; then
branch_hint="website"
fi
scope_hint=""
repo_scope="${repo_scope:-${TENANTATLAS_REPO_SCOPE:-}}"
case "${repo_scope}" in
""|repository)
;;
platform|website)
scope_hint="${repo_scope}"
;;
*)
fail_resolution "invalid-repository-scope" "repo_scope=${repo_scope}"
;;
esac
hint_family=""
hint_ambiguous=false
if [[ "${branch_hint}" == "ambiguous" ]]; then
hint_ambiguous=true
elif [[ -n "${branch_hint}" && -n "${scope_hint}" ]]; then
if [[ "${branch_hint}" != "${scope_hint}" ]]; then
hint_ambiguous=true
else
hint_family="${branch_hint}"
fi
fi
selected_family=""
family_source=""
if [[ -n "${explicit_family}" ]]; then
selected_family="${explicit_family}"
family_source="explicit"
fi
if [[ -n "${target_family}" ]]; then
if [[ -n "${selected_family}" && "${selected_family}" != "${target_family}" ]]; then
fail_resolution "branch-family-conflict" "explicit=${selected_family}" "pull_request_target=${target_family}"
fi
if [[ -z "${selected_family}" ]]; then
selected_family="${target_family}"
family_source="pull-request-target"
fi
fi
if [[ -n "${spec_family}" ]]; then
if [[ -n "${selected_family}" && "${selected_family}" != "${spec_family}" ]]; then
fail_resolution "branch-family-conflict" "selected=${selected_family}" "spec_metadata=${spec_family}" "spec_path=${spec_path}"
fi
if [[ -z "${selected_family}" ]]; then
selected_family="${spec_family}"
family_source="spec-metadata"
fi
fi
if [[ -n "${hint_family}" ]]; then
if [[ -z "${selected_family}" ]]; then
selected_family="${hint_family}"
family_source="branch-and-scope"
fi
fi
if [[ -z "${selected_family}" ]]; then
if [[ "${hint_ambiguous}" == true ]]; then
fail_resolution "ambiguous-branch-family" "branch_name=${branch_name}" "repo_scope=${repo_scope:-unset}" "spec_path=${spec_path:-unset}"
fi
fail_resolution "ambiguous-branch-family" "branch_name=${branch_name:-detached}" "repo_scope=${repo_scope:-unset}" "spec_path=${spec_path:-unset}"
fi
if [[ -z "${work_classification}" ]]; then
if [[ "${selected_family}" == "repository-wide" ]]; then
fail_resolution "explicit-promotion-classification-required" "pull_request_target=${resolved_target:-unset}"
fi
work_classification="${selected_family}"
fi
case "${work_classification}:${selected_family}" in
platform:platform|website:website|promotion:repository-wide|cross-stream:*|repository-governance:*)
;;
*)
fail_resolution "work-classification-family-conflict" "work_classification=${work_classification}" "branch_family=${selected_family}"
;;
esac
if [[ "${selected_family}" == "repository-wide" && "${work_classification}" != "promotion" && "${work_classification}" != "cross-stream" && "${work_classification}" != "repository-governance" ]]; then
fail_resolution "explicit-promotion-classification-required" "work_classification=${work_classification}"
fi
if [[ "${work_classification}" == "promotion" || "${work_classification}" == "cross-stream" || "${work_classification}" == "repository-governance" ]]; then
if [[ -z "${resolved_target}" && -z "${spec_declared_target}" ]]; then
fail_resolution "explicit-target-required" "work_classification=${work_classification}" "branch_family=${selected_family}"
fi
fi
expected_branch="$(expected_branch_for_family "${selected_family}")"
selected_ref=""
if [[ -n "${explicit_ref}" ]]; then
explicit_ref_family="$(family_for_branch "${explicit_ref}" || true)"
if [[ -z "${explicit_ref_family}" || "${explicit_ref_family}" != "${selected_family}" ]]; then
fail_resolution "wrong-family-ref" "expected_branch=${expected_branch}" "explicit_ref=${explicit_ref}"
fi
if ! git rev-parse --verify "${explicit_ref}^{commit}" >/dev/null 2>&1; then
fail_resolution "explicit-ref-unavailable" "expected_branch=${expected_branch}" "explicit_ref=${explicit_ref}"
fi
selected_ref="${explicit_ref}"
elif git rev-parse --verify "refs/remotes/origin/${expected_branch}^{commit}" >/dev/null 2>&1; then
selected_ref="origin/${expected_branch}"
elif git rev-parse --verify "refs/heads/${expected_branch}^{commit}" >/dev/null 2>&1; then
selected_ref="${expected_branch}"
else
fail_resolution "missing-family-ref" "branch_family=${selected_family}" "expected_branch=${expected_branch}" "attempted_refs=origin/${expected_branch},${expected_branch}"
fi
target_commit="$(git rev-parse --verify "${selected_ref}^{commit}" 2>/dev/null || true)"
if [[ -z "${target_commit}" ]]; then
fail_resolution "selected-ref-unavailable" "selected_ref=${selected_ref}"
fi
merge_base="$(git merge-base HEAD "${selected_ref}" 2>/dev/null || true)"
if [[ -z "${merge_base}" ]]; then
fail_resolution "no-common-merge-base" "selected_ref=${selected_ref}" "target_commit=${target_commit}"
fi
base_range="${merge_base}...HEAD"
if [[ "${format}" == "human" ]]; then
cat <<EOF
Feature base resolved.
Branch family: ${selected_family}
Work classification: ${work_classification}
Family source: ${family_source}
Expected integration branch: ${expected_branch}
Selected ref: ${selected_ref}
Target commit: ${target_commit}
Merge base: ${merge_base}
Base range: ${base_range}
EOF
else
cat <<EOF
status=resolved
branch_family=${selected_family}
work_classification=${work_classification}
family_source=${family_source}
expected_branch=${expected_branch}
selected_ref=${selected_ref}
target_commit=${target_commit}
merge_base=${merge_base}
base_range=${base_range}
EOF
fi