diff --git a/.github/workflows/repo_health.yml b/.github/workflows/repo_health.yml index f920ce0..4f6a198 100644 --- a/.github/workflows/repo_health.yml +++ b/.github/workflows/repo_health.yml @@ -29,7 +29,7 @@ on: workflow_dispatch: inputs: profile: - description: Which configuration profile to validate. release checks SFTP variables used by release pipeline. scripts checks baseline script prerequisites. repo runs repository health only. all runs release, scripts, and repo health. + description: Which configuration profile to validate. release checks SFTP variables used by release pipeline. scripts checks baseline script prerequisites. repo runs repository health only. al[...] required: true default: all type: choice @@ -82,6 +82,13 @@ env: # Operational toggles SFTP_VERBOSE: "false" + # File / directory variables (moved to top-level env) + DOCS_INDEX: docs/docs-index.md + SCRIPT_DIR: scripts + WORKFLOWS_DIR: .github/workflows + SHELLCHECK_PATTERN: '*.sh' + SPDX_FILE_GLOBS: '*.sh,*.php,*.js,*.ts,*.css,*.xml,*.yml,*.yaml' + jobs: access_check: name: Access control @@ -417,7 +424,7 @@ jobs: exit 0 fi - if [ ! -d scripts ]; then + if [ ! -d "${SCRIPT_DIR}" ]; then { printf '%s\n' '### Scripts governance' printf '%s\n' 'Status: OK (advisory)' @@ -445,7 +452,7 @@ jobs: [ "${d%/}" = "${a_norm}" ] && allowed=true done [ "${allowed}" = false ] && unapproved_dirs+=("${d%/}/") - done < <(find scripts -maxdepth 1 -mindepth 1 -type d 2>/dev/null | sed 's#^\./##') + done < <(find "${SCRIPT_DIR}" -maxdepth 1 -mindepth 1 -type d 2>/dev/null | sed 's#^\./##') { printf '%s\n' '### Scripts governance' @@ -682,8 +689,8 @@ jobs: fi # Workflow pinning advisory: flag uses @main/@master - if ls .github/workflows/*.yml >/dev/null 2>&1; then - bad_refs="$(grep -RIn --include='*.yml' --include='*.yaml' -E '^[[:space:]]*uses:[[:space:]]*[^#]+@(main|master)\b' .github/workflows 2>/dev/null || true)" + if ls "${WORKFLOWS_DIR}"/*.yml >/dev/null 2>&1 || ls "${WORKFLOWS_DIR}"/*.yaml >/dev/null 2>&1; then + bad_refs="$(grep -RIn --include='*.yml' --include='*.yaml' -E '^[[:space:]]*uses:[[:space:]]*[^#]+@(main|master)\b' "${WORKFLOWS_DIR}" 2>/dev/null || true)" if [ -n "${bad_refs}" ]; then extended_findings+=("Workflows reference actions @main/@master (pin versions): see log excerpt") { @@ -698,12 +705,12 @@ jobs: fi # Docs index link integrity (docs/docs-index.md) - if [ -f 'docs/docs-index.md' ]; then + if [ -f "${DOCS_INDEX}" ]; then missing_links="$(python3 - <<'PY' import os import re - idx = 'docs/docs-index.md' + idx = os.environ.get('DOCS_INDEX', 'docs/docs-index.md') base = os.getcwd() bad = [] @@ -742,7 +749,7 @@ jobs: fi # ShellCheck advisory - if [ -d 'scripts' ]; then + if [ -d "${SCRIPT_DIR}" ]; then if ! command -v shellcheck >/dev/null 2>&1; then sudo apt-get update -qq sudo apt-get install -y shellcheck >/dev/null @@ -755,7 +762,7 @@ jobs: if [ -n "${out_one}" ]; then sc_out="${sc_out}${out_one}\n" fi - done < <(find scripts -type f -name '*.sh' 2>/dev/null | sort) + done < <(find "${SCRIPT_DIR}" -type f -name "${SHELLCHECK_PATTERN}" 2>/dev/null | sort) if [ -n "${sc_out}" ]; then extended_findings+=("ShellCheck warnings detected (advisory)") @@ -772,12 +779,16 @@ jobs: # SPDX header advisory for common source types spdx_missing=() + IFS=',' read -r -a spdx_globs <<< "${SPDX_FILE_GLOBS}" + spdx_args=() + for g in "${spdx_globs[@]}"; do spdx_args+=("${g}"); done + while IFS= read -r f; do [ -z "${f}" ] && continue if ! head -n 40 "${f}" | grep -q 'SPDX-License-Identifier:'; then spdx_missing+=("${f}") fi - done < <(git ls-files '*.sh' '*.php' '*.js' '*.ts' '*.css' '*.xml' '*.yml' '*.yaml' 2>/dev/null || true) + done < <(git ls-files "${spdx_args[@]}" 2>/dev/null || true) if [ "${#spdx_missing[@]}" -gt 0 ]; then extended_findings+=("SPDX header missing in some tracked files (advisory)") @@ -791,7 +802,7 @@ jobs: # Git hygiene advisory: branches older than 180 days (remote) stale_cutoff_days=180 - stale_branches="$(git for-each-ref --format='%(refname:short) %(committerdate:unix)' refs/remotes/origin 2>/dev/null | awk -v now="$(date +%s)" -v days="${stale_cutoff_days}" '{if (now-$2 > days*86400) print $1}' | sed 's#^origin/##' | grep -v '^HEAD$' | head -n 50 || true)" + stale_branches="$(git for-each-ref --format='%(refname:short) %(committerdate:unix)' refs/remotes/origin 2>/dev/null | awk -v now="$(date +%s)" -v days="${stale_cutoff_days}" '{if (now-$2 [...] if [ -n "${stale_branches}" ]; then extended_findings+=("Stale remote branches detected (advisory)") {