## Copyright (C) 2026 - 2026 ENCRYPTED SUPPORT LLC <adrelanos@whonix.org>
## See the file COPYING for copying conditions.

## AI-Assisted

## Reusable workflow: CI mirror of agents/pre-push-static.sh.
##
## Runs the unified static-checks gate developers run locally as
## .git/hooks/pre-push. Local hook installation is opt-in per
## developer; this CI gate is mandatory and catches what the local
## hook misses (developers who didn't install it, AI sessions that
## didn't read the docs, environments lacking pre-commit-hooks).
##
## Checks enforced (see agents/pre-push-static.sh head comment for
## the authoritative list):
##   - bash -n on changed shell scripts
##   - shellcheck --external-sources on the same set
##   - R-001 ASCII on files + commit-range messages
##   - R-010, R-011, R-042, R-051, R-070, R-081, R-090, R-102,
##     R-120, R-130 (Tier-1 single-grep style-guide rules)
##   - pre-commit-hooks binaries (direct execution, no framework)
##     against the right file-type subsets: check-yaml,
##     check-json, check-toml, check-xml, check-ast, check-added-
##     large-files, check-merge-conflict, detect-aws-credentials,
##     detect-private-key, end-of-file-fixer, ...
##
## Runs in a Debian trixie container because:
##   * pre-commit-hooks is NOT in Debian bookworm (stable) apt.
##   * pre-commit-hooks is NOT in Ubuntu noble (24.04 = current
##     ubuntu-latest in GH Actions) apt either.
##   * pre-commit-hooks IS in Debian trixie apt (5.0.0+dfsg-5).
## apt-only by org preference; no pip fallback. shellcheck is also
## apt-installable in trixie, so a single container covers both.
##
## No 'concurrency:' block per the secrets-audit precedent:
## github.workflow inside a reusable resolves to the original
## caller's workflow name; a group declared here would deadlock
## with the caller's group.

name: pre-push static gate (reusable)

on:
  workflow_call:
    inputs:
      base-ref:
        description: 'Git ref to diff against. Default: PR base for pull_request, origin/master otherwise.'
        required: false
        type: string
        default: ''

permissions:
  contents: read

jobs:
  gate:
    name: pre-push static gate
    runs-on: ubuntu-latest
    container:
      image: debian:trixie-slim

    ## Fork-PR guard: a no-op unless the caller routes pull_request
    ## events into us, in which case it blocks PRs from forks. The
    ## dogfooding arm below checks out developer-meta-files at
    ## github.sha when the caller IS developer-meta-files - a fork
    ## PR there would mean attacker-controlled gate-script content
    ## runs in the runner. Same-repo branch PRs still run; those go
    ## through code review (members all have write) and are trusted
    ## by the threat model in agents/security.md. Matches the guard
    ## on reusable-bandit/codeql/scorecard/cppcheck. Closes CodeQL
    ## actions/untrusted-checkout/medium alert #15.
    if: github.event.pull_request.head.repo.full_name == github.repository || github.event_name != 'pull_request'

    timeout-minutes: 5
    permissions:
      contents: read
    env:
      ## Resolution order: explicit input > PR base ref > master
      ## fallback. Single GitHub expression keeps this thin per
      ## R-100 (no bash control flow in run: blocks).
      BASE_REF: >-
        ${{
          inputs.base-ref != '' && inputs.base-ref
          || (github.base_ref != '' && format('origin/{0}', github.base_ref) || 'origin/master')
        }}
      ## Dogfood ref: caller's commit when developer-meta-files is its
      ## own caller (so a PR that modifies the script under test gets
      ## exercised); 'master' for every other caller. Routed through
      ## env for the org-wide env-routing convention (same shape as
      ## BASE_REF / JOB_STATUS). The job-level fork-PR guard above is
      ## the actual mitigation for actions/untrusted-checkout.
      DMF_REF: ${{ github.repository == 'org-ai-assisted/developer-meta-files' && github.sha || 'master' }}
    steps:
      - name: Install dependencies (apt only)
        ## R-100: 4 lines, no control flow -- inline is acceptable.
        ## file: required by is_text_file()'s MIME-type fallback for
        ## extension-less text files.
        run: |
          apt-get update --quiet
          apt-get install --yes --no-install-recommends \
            ca-certificates file git pre-commit-hooks shellcheck
          git config --global --add safe.directory '*'

      - name: Checkout caller repo (full history for git diff)
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd  # v6.0.2
        with:
          fetch-depth: 0
          persist-credentials: false

      - name: Checkout developer-meta-files (gate script)
        if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd  # v6.0.2
        with:
          repository: org-ai-assisted/developer-meta-files
          ref: ${{ env.DMF_REF }}
          path: .github/dmf
          persist-credentials: false

      - name: Run pre-push-static.sh
        if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository
        run: ./.github/dmf/agents/pre-push-static.sh "${BASE_REF}"

      - name: Emit step summary
        if: always()
        ## Per-hook FAIL details remain in the gate step's log;
        ## this panel surfaces the headline result and the base ref
        ## so the PR Checks tab tells the reader what was diffed.
        ## R-100 compliant (no control flow).
        ##
        ## Template-substituted values (job.status here) MUST flow
        ## through env: so they cannot break out of the run: block
        ## via shell metacharacters - same pattern as #80/#84/#86.
        ## job.status itself is GitHub-controlled, but env routing
        ## is the org-wide convention regardless, and CodeQL flags
        ## inline ${{ ... }} in run: bodies.
        env:
          JOB_STATUS: ${{ job.status }}
        run: |
          .github/dmf/ci/step-summary-emit.sh \
            --tool 'pre-push static gate' \
            --column-header field \
            --row "outcome=${JOB_STATUS}" \
            --row "base-ref=${BASE_REF}"
