296 lines
11 KiB
Bash
Executable File
296 lines
11 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
|
|
set -euo pipefail
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
ROOT_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
|
GUARD_SOURCE="${ROOT_DIR}/scripts/check-ui-productization-coverage"
|
|
RESOLVER_SOURCE="${ROOT_DIR}/scripts/resolve-feature-base"
|
|
TEMP_ROOT="$(mktemp -d "${TMPDIR:-/tmp}/ui-productization-guard.XXXXXX")"
|
|
FAILED=0
|
|
|
|
trap 'rm -rf "${TEMP_ROOT}"' EXIT
|
|
|
|
write_file() {
|
|
local path="$1"
|
|
local content="$2"
|
|
|
|
mkdir -p "$(dirname "${path}")"
|
|
printf '%b' "${content}" > "${path}"
|
|
}
|
|
|
|
append_file() {
|
|
local path="$1"
|
|
local content="$2"
|
|
|
|
mkdir -p "$(dirname "${path}")"
|
|
printf '%b' "${content}" >> "${path}"
|
|
}
|
|
|
|
setup_repo() {
|
|
local case_id="$1"
|
|
local repo="${TEMP_ROOT}/${case_id}"
|
|
|
|
mkdir -p "${repo}"
|
|
git -C "${repo}" init -q
|
|
git -C "${repo}" config user.email "guard-validation@example.test"
|
|
git -C "${repo}" config user.name "Guard Validation"
|
|
|
|
mkdir -p "${repo}/scripts"
|
|
cp "${GUARD_SOURCE}" "${repo}/scripts/check-ui-productization-coverage"
|
|
cp "${RESOLVER_SOURCE}" "${repo}/scripts/resolve-feature-base"
|
|
chmod +x "${repo}/scripts/check-ui-productization-coverage" "${repo}/scripts/resolve-feature-base"
|
|
|
|
write_file "${repo}/apps/platform/app/Filament/Dashboard.php" "<?php\n// baseline\n"
|
|
write_file "${repo}/apps/platform/app/Support/Navigation/WorkspaceNavigation.php" "<?php\n// baseline\n"
|
|
write_file "${repo}/apps/platform/app/Providers/Filament/AdminPanelProvider.php" "<?php\n// baseline\n"
|
|
write_file "${repo}/apps/platform/app/Services/BackendOnlyService.php" "<?php\n// baseline\n"
|
|
write_file "${repo}/docs/ui-ux-enterprise-audit/route-inventory.md" "# Route Inventory\n"
|
|
write_file "${repo}/docs/process-note.md" "# Process Note\n"
|
|
write_file "${repo}/.specify/templates/spec-template.md" "# Baseline Template\n"
|
|
|
|
git -C "${repo}" add .
|
|
git -C "${repo}" commit -q -m "baseline"
|
|
git -C "${repo}" branch platform-dev
|
|
git -C "${repo}" update-ref refs/remotes/origin/platform-dev HEAD
|
|
git -C "${repo}" checkout -q -b feat/999-guard-validation
|
|
|
|
printf '%s\n' "${repo}"
|
|
}
|
|
|
|
touch_filament_page() {
|
|
local repo="$1"
|
|
|
|
append_file "${repo}/apps/platform/app/Filament/Dashboard.php" "// guarded UI change\n"
|
|
}
|
|
|
|
add_spec() {
|
|
local repo="$1"
|
|
local content="$2"
|
|
|
|
write_file "${repo}/specs/999-guard-validation/spec.md" "${content}"
|
|
}
|
|
|
|
case_ui_unchecked_template_heading() {
|
|
local repo="$1"
|
|
|
|
touch_filament_page "${repo}"
|
|
append_file "${repo}/.specify/templates/spec-template.md" "## UI Surface Impact\n\n- [ ] No UI surface impact\n- [ ] Navigation changed\n"
|
|
}
|
|
|
|
case_ui_unchecked_spec_checkbox() {
|
|
local repo="$1"
|
|
|
|
touch_filament_page "${repo}"
|
|
add_spec "${repo}" "# Spec\n\n## UI Surface Impact\n\n- [ ] Existing page changed\n\nRationale: unchecked should not pass.\n"
|
|
}
|
|
|
|
case_ui_checked_impact_spec() {
|
|
local repo="$1"
|
|
|
|
touch_filament_page "${repo}"
|
|
add_spec "${repo}" "# Spec\n\n## UI Surface Impact\n\n- [x] Existing page changed\n"
|
|
}
|
|
|
|
case_ui_checked_no_impact_with_rationale() {
|
|
local repo="$1"
|
|
|
|
touch_filament_page "${repo}"
|
|
add_spec "${repo}" "# Spec\n\n## UI Surface Impact\n\n- [x] No UI surface impact\n- [ ] Existing page changed\n\nRationale: guarded file changed for an internal refactor with identical rendered UI.\n"
|
|
}
|
|
|
|
case_ui_checked_no_impact_without_rationale() {
|
|
local repo="$1"
|
|
|
|
touch_filament_page "${repo}"
|
|
add_spec "${repo}" "# Spec\n\n## UI Surface Impact\n\n- [x] No UI surface impact\n- [ ] Existing page changed\n"
|
|
}
|
|
|
|
case_ui_real_audit_coverage_artifact() {
|
|
local repo="$1"
|
|
|
|
touch_filament_page "${repo}"
|
|
append_file "${repo}/docs/ui-ux-enterprise-audit/route-inventory.md" "\n| UI-999 | /admin/example | page | Example |\n"
|
|
}
|
|
|
|
case_navigation_provider_without_acknowledgment() {
|
|
local repo="$1"
|
|
|
|
append_file "${repo}/apps/platform/app/Support/Navigation/WorkspaceNavigation.php" "// navigation changed\n"
|
|
append_file "${repo}/apps/platform/app/Providers/Filament/AdminPanelProvider.php" "// provider changed\n"
|
|
}
|
|
|
|
case_navigation_provider_with_checked_acknowledgment() {
|
|
local repo="$1"
|
|
|
|
append_file "${repo}/apps/platform/app/Support/Navigation/WorkspaceNavigation.php" "// navigation changed\n"
|
|
append_file "${repo}/apps/platform/app/Providers/Filament/AdminPanelProvider.php" "// provider changed\n"
|
|
add_spec "${repo}" "# Spec\n\n## UI Surface Impact\n\n- [x] Navigation changed\n- [x] Filament panel/provider surface changed\n"
|
|
}
|
|
|
|
case_backend_only_diff() {
|
|
local repo="$1"
|
|
|
|
append_file "${repo}/apps/platform/app/Services/BackendOnlyService.php" "// backend-only change\n"
|
|
}
|
|
|
|
case_docs_only_diff() {
|
|
local repo="$1"
|
|
|
|
append_file "${repo}/docs/process-note.md" "\n## UI Surface Impact\n\nPlain documentation phrase without guarded UI changes.\n"
|
|
}
|
|
|
|
case_untracked_guarded_ui_without_acknowledgment() {
|
|
local repo="$1"
|
|
|
|
write_file "${repo}/apps/platform/app/Filament/NewUntrackedPage.php" "<?php\n// untracked guarded UI file\n"
|
|
}
|
|
|
|
case_committed_guarded_ui_without_acknowledgment() {
|
|
local repo="$1"
|
|
|
|
touch_filament_page "${repo}"
|
|
git -C "${repo}" add apps/platform/app/Filament/Dashboard.php
|
|
git -C "${repo}" commit -q -m "committed guarded UI change"
|
|
}
|
|
|
|
case_staged_guarded_ui_without_acknowledgment() {
|
|
local repo="$1"
|
|
|
|
touch_filament_page "${repo}"
|
|
git -C "${repo}" add apps/platform/app/Filament/Dashboard.php
|
|
}
|
|
|
|
run_case() {
|
|
local case_id="$1"
|
|
local expected="$2"
|
|
local setup_function="$3"
|
|
local repo
|
|
local status
|
|
local output_file="${TEMP_ROOT}/${case_id}.out"
|
|
|
|
repo="$(setup_repo "${case_id}")"
|
|
"${setup_function}" "${repo}"
|
|
|
|
set +e
|
|
(
|
|
cd "${repo}"
|
|
env \
|
|
-u TENANTATLAS_BRANCH_FAMILY \
|
|
-u TENANTATLAS_WORK_CLASSIFICATION \
|
|
-u TENANTATLAS_PR_TARGET \
|
|
-u TENANTATLAS_ACTIVE_SPEC \
|
|
-u TENANTATLAS_BASE_REF \
|
|
-u TENANTATLAS_REPO_SCOPE \
|
|
-u GITHUB_BASE_REF \
|
|
-u GITEA_BASE_REF \
|
|
bash scripts/check-ui-productization-coverage platform-dev --branch-family=platform
|
|
) >"${output_file}" 2>&1
|
|
status=$?
|
|
set -e
|
|
|
|
local expected_status=0
|
|
[[ "${expected}" == "coverage-fail" ]] && expected_status=1
|
|
[[ "${expected}" == "resolution-fail" ]] && expected_status=2
|
|
|
|
if [[ ${status} -eq ${expected_status} ]]; then
|
|
if [[ "${expected}" != "resolution-fail" ]] && ! grep -q '^branch_family=platform$' "${output_file}"; then
|
|
FAILED=1
|
|
printf 'FAIL %s did not print resolver family diagnostics\n' "${case_id}" >&2
|
|
sed 's/^/ /' "${output_file}" >&2
|
|
return
|
|
fi
|
|
|
|
if [[ "${expected}" != "resolution-fail" ]] && ! grep -q '^selected_ref=platform-dev$' "${output_file}"; then
|
|
FAILED=1
|
|
printf 'FAIL %s did not preserve the explicit family-matching ref\n' "${case_id}" >&2
|
|
sed 's/^/ /' "${output_file}" >&2
|
|
return
|
|
fi
|
|
|
|
if [[ "${expected}" != "resolution-fail" ]] && ! grep -q '^base_range=[0-9a-f]\{40\}\.\.\.HEAD$' "${output_file}"; then
|
|
FAILED=1
|
|
printf 'FAIL %s did not print a merge-base-derived range\n' "${case_id}" >&2
|
|
sed 's/^/ /' "${output_file}" >&2
|
|
return
|
|
fi
|
|
|
|
printf 'PASS %s (%s)\n' "${case_id}" "${expected}"
|
|
return
|
|
fi
|
|
|
|
FAILED=1
|
|
printf 'FAIL %s expected %s but exit status was %s\n' "${case_id}" "${expected}" "${status}" >&2
|
|
sed 's/^/ /' "${output_file}" >&2
|
|
}
|
|
|
|
run_resolution_failure_before_diff_case() {
|
|
local case_id="resolver_failure_stops_before_diff"
|
|
local repo
|
|
local status
|
|
local output_file="${TEMP_ROOT}/${case_id}.out"
|
|
local shim_directory="${TEMP_ROOT}/${case_id}-bin"
|
|
local diff_marker="${TEMP_ROOT}/${case_id}.diff-ran"
|
|
local real_git
|
|
|
|
repo="$(setup_repo "${case_id}")"
|
|
real_git="$(command -v git)"
|
|
mkdir -p "${shim_directory}"
|
|
write_file "${shim_directory}/git" '#!/usr/bin/env bash\nfor argument in "$@"; do\n if [[ "${argument}" == "diff" ]]; then\n : > "${DIFF_MARKER}"\n fi\ndone\nexec "${REAL_GIT}" "$@"\n'
|
|
chmod +x "${shim_directory}/git"
|
|
|
|
set +e
|
|
(
|
|
cd "${repo}"
|
|
export PATH="${shim_directory}:${PATH}"
|
|
export REAL_GIT="${real_git}"
|
|
export DIFF_MARKER="${diff_marker}"
|
|
env \
|
|
-u TENANTATLAS_BRANCH_FAMILY \
|
|
-u TENANTATLAS_WORK_CLASSIFICATION \
|
|
-u TENANTATLAS_PR_TARGET \
|
|
-u TENANTATLAS_ACTIVE_SPEC \
|
|
-u TENANTATLAS_BASE_REF \
|
|
-u TENANTATLAS_REPO_SCOPE \
|
|
-u GITHUB_BASE_REF \
|
|
-u GITEA_BASE_REF \
|
|
bash scripts/check-ui-productization-coverage origin/website-dev --branch-family=platform
|
|
) >"${output_file}" 2>&1
|
|
status=$?
|
|
set -e
|
|
|
|
if [[ ${status} -eq 2 ]] \
|
|
&& [[ ! -e "${diff_marker}" ]] \
|
|
&& grep -q '^reason=wrong-family-ref$' "${output_file}" \
|
|
&& grep -q '^diff_ran=false$' "${output_file}"; then
|
|
printf 'PASS %s (resolution-fail)\n' "${case_id}"
|
|
return
|
|
fi
|
|
|
|
FAILED=1
|
|
printf 'FAIL %s expected resolver exit 2 before any git diff\n' "${case_id}" >&2
|
|
sed 's/^/ /' "${output_file}" >&2
|
|
}
|
|
|
|
run_case "ui_unchecked_template_heading_fails" "coverage-fail" "case_ui_unchecked_template_heading"
|
|
run_case "ui_unchecked_spec_checkbox_fails" "coverage-fail" "case_ui_unchecked_spec_checkbox"
|
|
run_case "ui_checked_impact_spec_passes" "pass" "case_ui_checked_impact_spec"
|
|
run_case "ui_checked_no_impact_with_rationale_passes" "pass" "case_ui_checked_no_impact_with_rationale"
|
|
run_case "ui_checked_no_impact_without_rationale_fails" "coverage-fail" "case_ui_checked_no_impact_without_rationale"
|
|
run_case "ui_real_audit_coverage_artifact_passes" "pass" "case_ui_real_audit_coverage_artifact"
|
|
run_case "navigation_provider_without_acknowledgment_fails" "coverage-fail" "case_navigation_provider_without_acknowledgment"
|
|
run_case "navigation_provider_with_checked_acknowledgment_passes" "pass" "case_navigation_provider_with_checked_acknowledgment"
|
|
run_case "backend_only_diff_passes" "pass" "case_backend_only_diff"
|
|
run_case "docs_only_diff_passes" "pass" "case_docs_only_diff"
|
|
run_case "committed_guarded_ui_without_acknowledgment_fails" "coverage-fail" "case_committed_guarded_ui_without_acknowledgment"
|
|
run_case "staged_guarded_ui_without_acknowledgment_fails" "coverage-fail" "case_staged_guarded_ui_without_acknowledgment"
|
|
run_case "unstaged_guarded_ui_without_acknowledgment_fails" "coverage-fail" "case_ui_unchecked_template_heading"
|
|
run_case "untracked_guarded_ui_without_acknowledgment_fails" "coverage-fail" "case_untracked_guarded_ui_without_acknowledgment"
|
|
run_resolution_failure_before_diff_case
|
|
|
|
if [[ ${FAILED} -ne 0 ]]; then
|
|
exit 1
|
|
fi
|
|
|
|
printf 'All UI/Productization Coverage guard validation cases passed.\n'
|