diff --git a/.github/workflows/repo_health.yml b/.github/workflows/repo_health.yml index 15af385..e69de29 100644 --- a/.github/workflows/repo_health.yml +++ b/.github/workflows/repo_health.yml @@ -1,730 +0,0 @@ -# ============================================================================ -# Copyright (C) 2025 Moko Consulting -# -# This file is part of a Moko Consulting project. -# -# SPDX-License-Identifier: GPL-3.0-or-later -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# -# FILE INFORMATION -# DEFGROUP: GitHub.Workflow -# INGROUP: MokoStandards.Validation -# REPO: https://github.com/mokoconsulting-tech/MokoStandards -# PATH: /.github/workflows/repo_health.yml -# VERSION: 03.05.00 -# BRIEF: Enforces Joomla repository guardrails by validating release configuration, required validation scripts, tooling availability, and core repository health artifacts. -# ============================================================================ - -name: Joomla Repo Health - -concurrency: - group: repo-health-${{ github.repository }}-${{ github.ref }} - cancel-in-progress: true - -defaults: - run: - shell: bash - -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." - required: true - default: all - type: choice - options: - - all - - release - - scripts - - repo - pull_request: - paths: - - ".github/workflows/**" - - "scripts/**" - - "docs/**" - - "dev/**" - -permissions: - contents: read - -jobs: - access_check: - timeout-minutes: 10 - name: Access control - runs-on: ubuntu-latest - permissions: - contents: read - - outputs: - allowed: ${{ steps.perm.outputs.allowed }} - permission: ${{ steps.perm.outputs.permission }} - - steps: - - name: Check actor permission (admin only) - id: perm - uses: actions/github-script@v7 - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - script: | - const owner = context.repo.owner; - const repo = context.repo.repo; - const username = context.actor; - - const res = await github.rest.repos.getCollaboratorPermissionLevel({ - owner, - repo, - username, - }); - - const permission = (res?.data?.permission || "unknown").toLowerCase(); - const allowed = (permission === "admin"); - - core.setOutput("permission", permission); - core.setOutput("allowed", allowed ? "true" : "false"); - - const lines = []; - lines.push("### Access control"); - lines.push(""); - lines.push(`Actor: ${username}`); - lines.push(`Permission: ${permission}`); - lines.push(`Allowed: ${allowed}`); - lines.push(""); - lines.push("Policy: This workflow runs only for users with admin permission on the repository."); - await core.summary.addRaw(lines.join("\n")).write(); - - - name: Deny execution when not permitted - if: ${{ steps.perm.outputs.allowed != 'true' }} - run: | - set -euo pipefail - echo "ERROR: Access denied. Actor must have admin permission to run this workflow." >> "${GITHUB_STEP_SUMMARY}" - exit 1 - - release_config: - timeout-minutes: 20 - name: Release configuration - runs-on: ubuntu-latest - needs: [access_check] - if: ${{ needs.access_check.outputs.allowed == 'true' }} - permissions: - contents: read - - steps: - - name: Checkout - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - name: Guardrails - release secrets and vars - env: - PROFILE_RAW: "${{ github.event.inputs.profile }}" - FTP_HOST: "${{ secrets.FTP_HOST }}" - FTP_USER: "${{ secrets.FTP_USER }}" - FTP_KEY: "${{ secrets.FTP_KEY }}" - FTP_PASSWORD: "${{ secrets.FTP_PASSWORD }}" - FTP_PATH: "${{ secrets.FTP_PATH }}" - FTP_PROTOCOL: "${{ secrets.FTP_PROTOCOL }}" - FTP_PORT: "${{ secrets.FTP_PORT }}" - FTP_PATH_SUFFIX: "${{ vars.FTP_PATH_SUFFIX }}" - run: | - set -euo pipefail - - profile="${PROFILE_RAW:-all}" - case "${profile}" in - all|release|scripts|repo) ;; - *) - echo "ERROR: Unknown profile: ${profile}" >> "${GITHUB_STEP_SUMMARY}" - exit 1 - ;; - esac - - if [ "${profile}" = "scripts" ] || [ "${profile}" = "repo" ]; then - echo "Profile ${profile} selected. Skipping release configuration checks." >> "${GITHUB_STEP_SUMMARY}" - exit 0 - fi - - required=("FTP_HOST" "FTP_USER" "FTP_KEY" "FTP_PATH") - optional=("FTP_PASSWORD" "FTP_PROTOCOL" "FTP_PORT" "FTP_PATH_SUFFIX") - - missing=() - missing_optional=() - - for k in "${required[@]}"; do - v="${!k:-}" - [ -z "${v}" ] && missing+=("${k}") - done - - for k in "${optional[@]}"; do - v="${!k:-}" - [ -z "${v}" ] && missing_optional+=("${k}") - done - - proto="${FTP_PROTOCOL:-sftp}" - if [ -n "${FTP_PROTOCOL:-}" ] && [ "${proto}" != "sftp" ]; then - missing+=("FTP_PROTOCOL_INVALID") - fi - - if [ "${#missing_optional[@]}" -gt 0 ]; then - echo "### Missing optional release configuration" >> "${GITHUB_STEP_SUMMARY}" - for m in "${missing_optional[@]}"; do echo "- ${m}" >> "${GITHUB_STEP_SUMMARY}"; done - fi - - if [ "${#missing[@]}" -gt 0 ]; then - echo "### Missing required release configuration" >> "${GITHUB_STEP_SUMMARY}" - for m in "${missing[@]}"; do echo "- ${m}" >> "${GITHUB_STEP_SUMMARY}"; done - echo "ERROR: Guardrails failed. Missing required release configuration." >> "${GITHUB_STEP_SUMMARY}" - exit 1 - fi - - echo "### Guardrails: release configuration" >> "${GITHUB_STEP_SUMMARY}" - echo "All required release variables present." >> "${GITHUB_STEP_SUMMARY}" - - name: Guardrails - SFTP connectivity - env: - PROFILE_RAW: "${{ github.event.inputs.profile }}" - FTP_HOST: "${{ secrets.FTP_HOST }}" - FTP_USER: "${{ secrets.FTP_USER }}" - FTP_KEY: "${{ secrets.FTP_KEY }}" - FTP_PASSWORD: "${{ secrets.FTP_PASSWORD }}" - FTP_PORT: "${{ secrets.FTP_PORT }}" - run: | - set -euo pipefail - - profile="${PROFILE_RAW:-all}" - case "${profile}" in - all|release|scripts|repo) ;; - *) - echo "ERROR: Unknown profile: ${profile}" >> "${GITHUB_STEP_SUMMARY}" - exit 1 - ;; - esac - - if [ "${profile}" = "scripts" ] || [ "${profile}" = "repo" ]; then - echo "Profile ${profile} selected. Skipping SFTP connectivity check." >> "${GITHUB_STEP_SUMMARY}" - exit 0 - fi - - mkdir -p "$HOME/.ssh" - key_file="$HOME/.ssh/ci_sftp_key" - printf '%s\n' "${FTP_KEY}" > "${key_file}" - chmod 600 "${key_file}" - - # If FTP_PASSWORD is present, treat it as the private key passphrase and decrypt the key in place. - # If FTP_PASSWORD is empty, the key must already be unencrypted. - if [ -n "${FTP_PASSWORD:-}" ]; then - first_line="$(head -n 1 "${key_file}" || true)" - if printf '%s' "${first_line}" | grep -q '^PuTTY-User-Key-File-'; then - echo "ERROR: FTP_KEY appears to be a PuTTY PPK. Provide an OpenSSH private key to use FTP_PASSWORD decryption." >> "${GITHUB_STEP_SUMMARY}" - exit 1 - fi - - ssh-keygen -p -P "${FTP_PASSWORD}" -N "" -f "${key_file}" >/dev/null - fi - - port="${FTP_PORT:-22}" - - echo "### SFTP connectivity test" >> "${GITHUB_STEP_SUMMARY}" - echo "Attempting non-destructive SFTP session (pwd only)." >> "${GITHUB_STEP_SUMMARY}" - - printf 'pwd\nbye\n' | sftp -oBatchMode=yes -oStrictHostKeyChecking=no -P "${port}" -i "${key_file}" "${FTP_USER}@${FTP_HOST}" >/tmp/sftp_check.log 2>&1 - sftp_rc=$? - - if [ "${sftp_rc}" -eq 0 ]; then - echo "### SFTP connectivity result" >> "${GITHUB_STEP_SUMMARY}" - echo "Status: SUCCESS" >> "${GITHUB_STEP_SUMMARY}" - else - echo "### SFTP connectivity result" >> "${GITHUB_STEP_SUMMARY}" - echo "Status: FAILED (exit code ${sftp_rc})" >> "${GITHUB_STEP_SUMMARY}" - echo "" >> "${GITHUB_STEP_SUMMARY}" - echo "Last SFTP output:" >> "${GITHUB_STEP_SUMMARY}" - tail -n 10 /tmp/sftp_check.log >> "${GITHUB_STEP_SUMMARY}" - exit 1 - fi - - scripts_config: - name: Scripts and tooling - runs-on: ubuntu-latest - timeout-minutes: 15 - needs: [access_check] - if: ${{ needs.access_check.outputs.allowed == 'true' }} - permissions: - contents: read - - steps: - - name: Checkout - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Guardrails - scripts folder governance - env: - PROFILE_RAW: "${{ github.event.inputs.profile }}" - run: | - set -euo pipefail - - profile="${PROFILE_RAW:-all}" - case "${profile}" in - all|release|scripts|repo) ;; - *) - echo "ERROR: Unknown profile: ${profile}" >> "${GITHUB_STEP_SUMMARY}" - exit 1 - ;; - esac - - if [ "${profile}" = "release" ] || [ "${profile}" = "repo" ]; then - echo "Profile ${profile} selected. Skipping scripts checks." >> "${GITHUB_STEP_SUMMARY}" - exit 0 - fi - - required_script_dirs=( - "scripts/fix" - "scripts/lib" - "scripts/release" - "scripts/run" - "scripts/validate" - ) - - required_script_files=( - "scripts/validate/manifest.sh" - "scripts/validate/xml_wellformed.sh" - "scripts/validate/changelog.sh" - "scripts/validate/tabs.sh" - "scripts/validate/paths.sh" - "scripts/validate/version_alignment.sh" - "scripts/validate/language_structure.sh" - "scripts/validate/php_syntax.sh" - "scripts/validate/no_secrets.sh" - "scripts/validate/license_headers.sh" - ) - - missing_dirs=() - missing_files=() - - for d in "${required_script_dirs[@]}"; do - [ ! -d "${d}" ] && missing_dirs+=("${d}/") - done - - unapproved_dirs=() - while IFS= read -r d; do - case "${d}" in - scripts|scripts/fix|scripts/lib|scripts/release|scripts/run|scripts/validate) ;; - *) unapproved_dirs+=("${d}/") ;; - esac - done < <(find scripts -maxdepth 1 -mindepth 1 -type d 2>/dev/null | sed 's#^\./##') - - for f in "${required_script_files[@]}"; do - [ ! -f "${f}" ] && missing_files+=("${f}") - done - - legacy_glob_found=() - while IFS= read -r f; do - [ -n "${f}" ] && legacy_glob_found+=("${f}") - done < <(find scripts -maxdepth 1 -type f -name 'validate_*.sh' 2>/dev/null || true) - - tools_to_install=() - command -v php >/dev/null 2>&1 || tools_to_install+=("php-cli") - command -v xmllint >/dev/null 2>&1 || tools_to_install+=("libxml2-utils") - command -v shellcheck >/dev/null 2>&1 || tools_to_install+=("shellcheck") - - if [ "${#tools_to_install[@]}" -gt 0 ]; then - echo "Installing missing tools: ${tools_to_install[*]}" >> "${GITHUB_STEP_SUMMARY}" - sudo apt-get update -y - sudo apt-get install -y ${tools_to_install[*]} - fi - - tool_status=() - command -v php >/dev/null 2>&1 && tool_status+=("php") || true - command -v xmllint >/dev/null 2>&1 && tool_status+=("xmllint") || true - command -v shellcheck >/dev/null 2>&1 && tool_status+=("shellcheck") || true - - export MISSING_DIRS="$(printf '%s\\n' "${missing_dirs[@]:-}")" - export MISSING_FILES="$(printf '%s\\n' "${missing_files[@]:-}")" - export TOOLS="${tool_status[*]:-}" - - report_json="$(python3 - <<'PY' - import json - import os - - profile = os.environ.get('PROFILE_RAW') or 'all' - required_script_dirs = [ - "scripts/fix", - "scripts/lib", - "scripts/release", - "scripts/run", - "scripts/validate", - ] - required_script_files = [ - "scripts/validate/manifest.sh", - "scripts/validate/xml_wellformed.sh", - "scripts/validate/changelog.sh", - "scripts/validate/tabs.sh", - "scripts/validate/paths.sh", - "scripts/validate/version_alignment.sh", - "scripts/validate/language_structure.sh", - "scripts/validate/php_syntax.sh", - "scripts/validate/no_secrets.sh", - "scripts/validate/license_headers.sh", - ] - - missing_dirs = os.environ.get('MISSING_DIRS','').split(' - ') if os.environ.get('MISSING_DIRS') else [] - missing_files = os.environ.get('MISSING_FILES','').split(' - ') if os.environ.get('MISSING_FILES') else [] - tools = os.environ.get('TOOLS','').split() if os.environ.get('TOOLS') else [] - - out = { - "profile": profile, - "checked": { - "required_script_dirs": required_script_dirs, - "required_script_files": required_script_files, - }, - "missing_dirs": [x for x in missing_dirs if x], - "missing_files": [x for x in missing_files if x], - "tools_available": tools, - } - - print(json.dumps(out, indent=2)) - PY - )" - - { - echo "### Guardrails: scripts and tooling" - echo "Tools available: ${tool_status[*]:-none}" - echo "" - echo "### Guardrails report (JSON)" - echo "```json" - echo "${report_json}" - echo "```" - } >> "${GITHUB_STEP_SUMMARY}" - - if [ "${#missing_dirs[@]}" -gt 0 ]; then - echo "### Missing required script directories" >> "${GITHUB_STEP_SUMMARY}" - for m in "${missing_dirs[@]}"; do echo "- ${m}" >> "${GITHUB_STEP_SUMMARY}"; done - echo "ERROR: Guardrails failed. Missing required script directories." >> "${GITHUB_STEP_SUMMARY}" - exit 1 - fi - - if [ "${#unapproved_dirs[@]}" -gt 0 ]; then - echo "### Unapproved script directories detected" >> "${GITHUB_STEP_SUMMARY}" - for m in "${unapproved_dirs[@]}"; do echo "- ${m}" >> "${GITHUB_STEP_SUMMARY}"; done - echo "ERROR: Guardrails failed. Only fix, lib, release, run, validate directories are allowed under scripts/." >> "${GITHUB_STEP_SUMMARY}" - exit 1 - fi - - if [ "${#missing_files[@]}" -gt 0 ]; then - echo "### Missing script files" >> "${GITHUB_STEP_SUMMARY}" - for m in "${missing_files[@]}"; do echo "- ${m}" >> "${GITHUB_STEP_SUMMARY}"; done - echo "ERROR: Guardrails failed. Missing required script files." >> "${GITHUB_STEP_SUMMARY}" - exit 1 - fi - - if [ "${#legacy_glob_found[@]}" -gt 0 ]; then - echo "### Legacy validate_* scripts detected at scripts/ root (disallowed)" >> "${GITHUB_STEP_SUMMARY}" - for m in "${legacy_glob_found[@]}"; do echo "- ${m}" >> "${GITHUB_STEP_SUMMARY}"; done - echo "ERROR: Guardrails failed. Move scripts into scripts/validate/ with approved filenames." >> "${GITHUB_STEP_SUMMARY}" - exit 1 - fi - - non_exec=() - while IFS= read -r f; do - [ -n "${f}" ] && non_exec+=("${f}") - done < <(find scripts -type f -name '*.sh' ! -perm -u=x 2>/dev/null || true) - - if [ "${#non_exec[@]}" -gt 0 ]; then - echo "### Non-executable shell scripts detected" >> "${GITHUB_STEP_SUMMARY}" - for m in "${non_exec[@]}"; do echo "- ${m}" >> "${GITHUB_STEP_SUMMARY}"; done - echo "ERROR: Guardrails failed. All scripts/**/*.sh must be executable." >> "${GITHUB_STEP_SUMMARY}" - exit 1 - fi - - sh_files="$(find scripts -type f -name '*.sh' 2>/dev/null || true)" - - if [ -z "${sh_files}" ]; then - echo "No shell scripts found under scripts/." >> "${GITHUB_STEP_SUMMARY}" - echo "Shell quality gate skipped." >> "${GITHUB_STEP_SUMMARY}" - exit 0 - fi - - while IFS= read -r f; do - [ -z "${f}" ] && continue - bash -n "${f}" - done <<< "${sh_files}" - - shellcheck -x ${sh_files} - - echo "Shell quality gate passed." >> "${GITHUB_STEP_SUMMARY}" - - { - echo "### Guardrails: scripts and tooling" - echo "Tools available: ${tool_status[*]:-none}" - echo "" - echo "### Guardrails report (JSON)" - echo "```json" - echo "${report_json}" - echo "```" - } >> "${GITHUB_STEP_SUMMARY}" - - if [ "${#missing_dirs[@]}" -gt 0 ]; then - echo "### Missing required script directories" >> "${GITHUB_STEP_SUMMARY}" - for m in "${missing_dirs[@]}"; do echo "- ${m}" >> "${GITHUB_STEP_SUMMARY}"; done - echo "ERROR: Guardrails failed. Missing required script directories." >> "${GITHUB_STEP_SUMMARY}" - exit 1 - fi - - if [ "${#unapproved_dirs[@]}" -gt 0 ]; then - echo "### Unapproved script directories detected" >> "${GITHUB_STEP_SUMMARY}" - for m in "${unapproved_dirs[@]}"; do echo "- ${m}" >> "${GITHUB_STEP_SUMMARY}"; done - echo "ERROR: Guardrails failed. Only fix, lib, release, run, validate directories are allowed under scripts/." >> "${GITHUB_STEP_SUMMARY}" - exit 1 - fi - - if [ "${#missing_files[@]}" -gt 0 ]; then - echo "### Missing script files" >> "${GITHUB_STEP_SUMMARY}" - for m in "${missing_files[@]}"; do echo "- ${m}" >> "${GITHUB_STEP_SUMMARY}"; done - echo "ERROR: Guardrails failed. Missing required script files." >> "${GITHUB_STEP_SUMMARY}" - exit 1 - fi - - if [ "${#legacy_glob_found[@]}" -gt 0 ]; then - echo "### Legacy validate_* scripts detected at scripts/ root (disallowed)" >> "${GITHUB_STEP_SUMMARY}" - for m in "${legacy_glob_found[@]}"; do echo "- ${m}" >> "${GITHUB_STEP_SUMMARY}"; done - echo "ERROR: Guardrails failed. Move scripts into scripts/validate/ with approved filenames." >> "${GITHUB_STEP_SUMMARY}" - exit 1 - fi - - non_exec=() - while IFS= read -r f; do - [ -n "${f}" ] && non_exec+=("${f}") - done < <(find scripts -type f -name '*.sh' ! -perm -u=x 2>/dev/null || true) - - if [ "${#non_exec[@]}" -gt 0 ]; then - echo "### Non-executable shell scripts detected" >> "${GITHUB_STEP_SUMMARY}" - for m in "${non_exec[@]}"; do echo "- ${m}" >> "${GITHUB_STEP_SUMMARY}"; done - echo "ERROR: Guardrails failed. All scripts/**/*.sh must be executable." >> "${GITHUB_STEP_SUMMARY}" - exit 1 - fi - - sh_files="$(find scripts -type f -name '*.sh' 2>/dev/null || true)" - - if [ -z "${sh_files}" ]; then - echo "No shell scripts found under scripts/." >> "${GITHUB_STEP_SUMMARY}" - echo "Shell quality gate skipped." >> "${GITHUB_STEP_SUMMARY}" - exit 0 - fi - - while IFS= read -r f; do - [ -z "${f}" ] && continue - bash -n "${f}" - done <<< "${sh_files}" - - shellcheck -x ${sh_files} - - echo "Shell quality gate passed." >> "${GITHUB_STEP_SUMMARY}" - - repo_health: - name: Repository health - runs-on: ubuntu-latest - timeout-minutes: 15 - needs: [access_check] - if: ${{ needs.access_check.outputs.allowed == 'true' }} - permissions: - contents: read - - steps: - - name: Checkout - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Repo health checks - env: - PROFILE_RAW: "${{ github.event.inputs.profile }}" - run: | - set -euo pipefail - - profile="${PROFILE_RAW:-all}" - case "${profile}" in - all|release|scripts|repo) ;; - *) - echo "ERROR: Unknown profile: ${profile}" >> "${GITHUB_STEP_SUMMARY}" - exit 1 - ;; - esac - - required_files=( - "README.md" - "LICENSE" - "CHANGELOG.md" - "CONTRIBUTING.md" - "CODE_OF_CONDUCT.md" - "TODO.md" - "docs/docs-index.md" - ) - - optional_files=( - "SECURITY.md" - "GOVERNANCE.md" - ".editorconfig" - ".gitattributes" - ".gitignore" - ) - - required_paths=( - ".github/workflows" - "scripts" - "docs" - "dev" - ) - - missing_required=() - missing_optional=() - - for f in "${required_files[@]}"; do - [ ! -f "${f}" ] && missing_required+=("${f}") - done - - for f in "${optional_files[@]}"; do - [ ! -f "${f}" ] && missing_optional+=("${f}") - done - - for p in "${required_paths[@]}"; do - [ ! -d "${p}" ] && missing_required+=("${p}/") - done - - if [ -d "src" ]; then - missing_required+=("src/ (disallowed, use dev/ only)") - fi - - git fetch origin --prune - - dev_paths=() - dev_branches=() - - while IFS= read -r b; do - name="${b#origin/}" - if [ "${name}" = "dev" ]; then - dev_branches+=("${name}") - else - dev_paths+=("${name}") - fi - done < <(git branch -r --list "origin/dev*" | sed 's/^ *//') - - if [ "${#dev_paths[@]}" -eq 0 ]; then - missing_required+=("dev/* branch (e.g. dev/01.00.00)") - fi - - if [ "${#dev_branches[@]}" -gt 0 ]; then - missing_required+=("invalid branch 'dev' (must be dev/)") - fi - - content_warnings=() - - if [ -f "CHANGELOG.md" ] && ! grep -Eq '^# Changelog' CHANGELOG.md; then - content_warnings+=("CHANGELOG.md missing '# Changelog' header") - fi - - if [ -f "LICENSE" ] && ! grep -qiE 'GNU GENERAL PUBLIC LICENSE|GPL' LICENSE; then - content_warnings+=("LICENSE does not look like a GPL text") - fi - - if [ -f "README.md" ] && ! grep -qiE 'moko|Moko' README.md; then - content_warnings+=("README.md missing expected brand keyword") - fi - - export MISSING_REQUIRED="$(printf '%s\\n' "${missing_required[@]:-}")" - export MISSING_OPTIONAL="$(printf '%s\\n' "${missing_optional[@]:-}")" - export CONTENT_WARNINGS="$(printf '%s\\n' "${content_warnings[@]:-}")" - - report_json="$(python3 - <<'PY' - import json - import os - - profile = os.environ.get('PROFILE_RAW') or 'all' - required_files = ["README.md","LICENSE","CHANGELOG.md","CONTRIBUTING.md","CODE_OF_CONDUCT.md","TODO.md","docs/docs-index.md"] - optional_files = ["SECURITY.md","GOVERNANCE.md",".editorconfig",".gitattributes",".gitignore"] - required_paths = [".github/workflows","scripts","docs","dev"] - - missing_required = os.environ.get('MISSING_REQUIRED','').split(' - ') if os.environ.get('MISSING_REQUIRED') else [] - missing_optional = os.environ.get('MISSING_OPTIONAL','').split(' - ') if os.environ.get('MISSING_OPTIONAL') else [] - content_warnings = os.environ.get('CONTENT_WARNINGS','').split(' - ') if os.environ.get('CONTENT_WARNINGS') else [] - - out = { - "profile": profile, - "checked": { - "required_files": required_files, - "optional_files": optional_files, - "required_paths": required_paths, - }, - "missing_required": [x for x in missing_required if x], - "missing_optional": [x for x in missing_optional if x], - "content_warnings": [x for x in content_warnings if x], - } - - print(json.dumps(out, indent=2)) - PY - )" - - { - echo "### Guardrails: repository health" - echo "" - echo "### Guardrails report (JSON)" - echo "```json" - echo "${report_json}" - echo "```" - } >> "${GITHUB_STEP_SUMMARY}" - - if [ "${#missing_required[@]}" -gt 0 ]; then - echo "### Missing required repo artifacts" >> "${GITHUB_STEP_SUMMARY}" - for m in "${missing_required[@]}"; do echo "- ${m}" >> "${GITHUB_STEP_SUMMARY}"; done - echo "ERROR: Guardrails failed. Missing required repository artifacts." >> "${GITHUB_STEP_SUMMARY}" - exit 1 - fi - - if [ "${#missing_optional[@]}" -gt 0 ]; then - echo "### Missing optional repo artifacts" >> "${GITHUB_STEP_SUMMARY}" - for m in "${missing_optional[@]}"; do echo "- ${m}" >> "${GITHUB_STEP_SUMMARY}"; done - fi - - if [ "${#content_warnings[@]}" -gt 0 ]; then - echo "### Repo content warnings" >> "${GITHUB_STEP_SUMMARY}" - for m in "${content_warnings[@]}"; do echo "- ${m}" >> "${GITHUB_STEP_SUMMARY}"; done - fi - - { - echo "### Guardrails: repository health" - echo "" - echo "### Guardrails report (JSON)" - echo "```json" - echo "${report_json}" - echo "```" - } >> "${GITHUB_STEP_SUMMARY}" - - if [ "${#missing_required[@]}" -gt 0 ]; then - echo "### Missing required repo artifacts" >> "${GITHUB_STEP_SUMMARY}" - for m in "${missing_required[@]}"; do echo "- ${m}" >> "${GITHUB_STEP_SUMMARY}"; done - echo "ERROR: Guardrails failed. Missing required repository artifacts." >> "${GITHUB_STEP_SUMMARY}" - exit 1 - fi - - if [ "${#missing_optional[@]}" -gt 0 ]; then - echo "### Missing optional repo artifacts" >> "${GITHUB_STEP_SUMMARY}" - for m in "${missing_optional[@]}"; do echo "- ${m}" >> "${GITHUB_STEP_SUMMARY}"; done - fi - - if [ "${#content_warnings[@]}" -gt 0 ]; then - echo "### Repo content warnings" >> "${GITHUB_STEP_SUMMARY}" - for m in "${content_warnings[@]}"; do echo "- ${m}" >> "${GITHUB_STEP_SUMMARY}"; done - fi