# Forgejo Actions workflow for pg_tre on Codeberg.
#
# Runs on Codeberg's CI runners (https://codeberg.org/actions).
# Forgejo Actions is GitHub Actions-compatible, so this workflow
# mirrors .github/workflows/ci.yml; the GitHub side is for the
# read-only mirror.
name: CI

on:
  push:
    branches: [main]
    tags: ['v*']
  pull_request:

jobs:
  build-and-test:
    name: build PG${{ matrix.pg }} / ${{ matrix.cc }}
    runs-on: docker
    container:
      image: debian:12
    strategy:
      fail-fast: false
      matrix:
        pg: ['18']
        cc: [gcc, clang]
    steps:
      - uses: actions/checkout@v4
        with:
          submodules: true

      - name: Install build deps
        run: |
          apt-get update
          apt-get install -y --no-install-recommends \
              ca-certificates curl gnupg lsb-release \
              autoconf automake libtool gettext autopoint m4 \
              build-essential pkg-config git clang sudo systemd-coredump
          install -d /usr/share/postgresql-common/pgdg
          curl -o /usr/share/postgresql-common/pgdg/apt.postgresql.org.asc \
              https://www.postgresql.org/media/keys/ACCC4CF8.asc
          . /etc/os-release
          sh -c 'echo "deb [signed-by=/usr/share/postgresql-common/pgdg/apt.postgresql.org.asc] https://apt.postgresql.org/pub/repos/apt ${VERSION_CODENAME}-pgdg main" > /etc/apt/sources.list.d/pgdg.list'
          apt-get update
          apt-get install -y --no-install-recommends \
              postgresql-server-dev-${{ matrix.pg }} \
              postgresql-${{ matrix.pg }} \
              postgresql-${{ matrix.pg }}-pgwalinspect

      - name: Build with Werror
        env:
          CC: ${{ matrix.cc }}
        run: |
          PG_CONFIG=$(which pg_config) make \
              PG_CFLAGS="-Werror -Wno-error=unused-parameter"

      - name: Install
        run: PG_CONFIG=$(which pg_config) make install

      - name: Start PostgreSQL
        run: |
          service postgresql start
          su - postgres -c "createdb $(whoami)" || true

      - name: Run regression tests (3x for flake detection)
        run: |
          for i in 1 2 3; do
              echo "==> Regression run $i/3"
              PG_CONFIG=$(which pg_config) make localcheck \
                  || PG_CONFIG=$(which pg_config) make check
          done

      - name: Run wal_audit.sh
        run: PG_CONFIG=$(which pg_config) bash test/scripts/wal_audit.sh

      - name: Run replication.sh
        run: PG_CONFIG=$(which pg_config) bash test/scripts/replication.sh

      - name: Upload regression artifacts on failure
        if: failure()
        uses: actions/upload-artifact@v3
        with:
          name: regression-${{ matrix.cc }}-pg${{ matrix.pg }}
          path: |
            test/results/
            /var/log/postgresql/
          retention-days: 7

  asan:
    # Regression suite inside an AddressSanitizer-instrumented PostgreSQL.
    # The SERVER is instrumented, not just pg_tre: the 4.2.0 similarity
    # crash was an out-of-bounds store made by PostgreSQL's own
    # pg_mb2wchar_with_len() into a pg_tre stack variable, which ASan on
    # pg_tre.so alone does not see.  scripts/asan-check.sh is the whole
    # recipe, so a local run is the same check as CI.
    #
    # Validated: against the 4.1.0 similarity code this job exits 2 with
    # "stack-buffer-overflow ... trgm_set src/query/trgm_similarity.c:94";
    # against HEAD it passes 47/47 with no report.
    #
    # Exit codes: 1 = test failure, 2 = sanitizer report, 3 = setup.
    name: ASan (instrumented PG${{ matrix.pg }})
    runs-on: docker
    container:
      image: debian:12
    timeout-minutes: 60
    strategy:
      fail-fast: false
      matrix:
        pg: ['18.0']
    steps:
      - uses: actions/checkout@v4
        with:
          submodules: true

      - name: Install build deps
        run: |
          apt-get update
          apt-get install -y --no-install-recommends \
              ca-certificates curl bzip2 build-essential pkg-config git \
              flex bison autoconf automake libtool gettext autopoint m4 \
              locales

      - name: Cache ASan PostgreSQL ${{ matrix.pg }}
        id: cache-pg
        uses: actions/cache@v4
        with:
          path: /opt/pg-asan-${{ matrix.pg }}
          key: pg-asan-${{ matrix.pg }}-debian12-${{ hashFiles('scripts/asan-check.sh') }}

      - name: Build + run under ASan
        env:
          PG_VERSION: ${{ matrix.pg }}
          ASAN_PREFIX: /opt/pg-asan-${{ matrix.pg }}
          ASAN_WORK: /tmp/pg_tre-asan
        run: |
          # initdb refuses to run as root; the container runs as root.
          useradd -m builder
          mkdir -p "$ASAN_PREFIX" "$ASAN_WORK"
          chown -R builder "$ASAN_PREFIX" "$ASAN_WORK" "$GITHUB_WORKSPACE"
          su builder -c "PG_VERSION=$PG_VERSION ASAN_PREFIX=$ASAN_PREFIX \
              ASAN_WORK=$ASAN_WORK bash scripts/asan-check.sh"

      - name: Upload sanitizer reports on failure
        if: failure()
        uses: actions/upload-artifact@v3
        with:
          name: asan-pg${{ matrix.pg }}
          path: |
            /tmp/pg_tre-asan/reports/
            /tmp/pg_tre-asan/server.log
            /tmp/pg_tre-asan/regress.log
            test/results/
          retention-days: 14
  format-check:
    name: clang-format on changed files
    runs-on: docker
    container:
      image: debian:12
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Install clang-format
        run: |
          apt-get update
          apt-get install -y --no-install-recommends git clang-format

      - name: Check formatting on changed files
        run: |
          BASE=$(git merge-base HEAD origin/main 2>/dev/null \
              || git rev-parse HEAD~1)
          BASE="$BASE" make format-changed

  pgspot:
    name: extension SQL security scan
    runs-on: docker
    container:
      image: debian:12
    steps:
      - uses: actions/checkout@v4

      - name: Install pgspot
        run: |
          apt-get update
          apt-get install -y --no-install-recommends \
              python3 python3-pip python3-venv git
          python3 -m venv /opt/venv
          /opt/venv/bin/pip install pgspot

      - name: Scan current default-version SQL
        run: |
          VERSION=$(awk -F"'" '/default_version/ {print $2}' pg_tre.control)
          /opt/venv/bin/pgspot "sql/pg_tre--${VERSION}.sql"

      - name: Scan all upgrade scripts
        run: |
          for f in sql/pg_tre--*--*.sql; do
              echo ""
              echo "==> Scanning $f"
              /opt/venv/bin/pgspot "$f"
          done

  lint-docs:
    name: lint docs
    runs-on: docker
    container:
      image: debian:12
    steps:
      - uses: actions/checkout@v4
        with:
          submodules: false

      - name: Install lint deps
        run: |
          apt-get update
          apt-get install -y --no-install-recommends \
              ca-certificates curl npm git
          npm install -g markdownlint-cli || true

      - name: Lint Markdown
        run: |
          markdownlint \
              -c .markdownlint.json \
              '**/*.md' '!vendor/**' '!build/**' '!bench/corpus.csv' \
              || echo "markdownlint reported issues (non-fatal for now)"

      - name: Verify no committed binary artifacts
        run: |
          if git ls-files | grep -E '\.(o|so|dylib|a)$' ; then
              echo "FAIL: committed binary artifacts"
              exit 1
          fi

      - name: Verify flake pins match the submodule gitlinks
        run: |
          # The flake pins vendor/tre and vendor/lime as its own inputs
          # rather than using ?submodules=1, so every vendored rev has two
          # sources of truth.  When they diverged, `make` built fine and
          # passed 41/41 tests while `nix build .#pg18` died in patchPhase
          # (the TRE progress-hook patch is generated against one specific
          # TRE tree).  v3.2.3 shipped exactly that way.
          #
          # The GitHub mirror had this check, but GitHub is a push-mirror of
          # Codeberg and mirror syncs do not fire push events -- so it never
          # ran on the release.  It belongs here, where CI actually runs.
          bash scripts/flake-check-revs.sh

      - name: Verify STATUS.md is populated
        run: |
          test -s STATUS.md && grep -qE "Released:|## What ships" STATUS.md

  release-dist:
    name: build release tarball
    runs-on: docker
    container:
      image: debian:12
    needs: [build-and-test]
    if: startsWith(github.ref, 'refs/tags/v')
    steps:
      - uses: actions/checkout@v4
        with:
          submodules: true

      - name: Install deps
        run: |
          apt-get update
          apt-get install -y --no-install-recommends git make tar gzip

      - name: Build distribution tarball
        run: make dist

      - name: Upload release tarball
        uses: actions/upload-artifact@v3
        with:
          name: pg_tre-tarball
          path: pg_tre-*.tar.gz
          retention-days: 90

  publish-pages:
    # Publishes the rendered mdBook to Codeberg Pages via the official
    # git-pages action, which uploads to the wildcard site
    # https://<owner>.codeberg.page/<repo>/ using the automatic
    # ${{ forge.token }} (Forge Wildcard Authorization) -- no secret or
    # SSH deploy key required.  See https://codeberg.org/git-pages/action
    name: publish docs to Codeberg Pages
    runs-on: docker
    container:
      image: debian:12
    needs: [lint-docs]
    if: github.event_name == 'push' && github.ref == 'refs/heads/main'
    steps:
      - uses: actions/checkout@v4
        with:
          submodules: false

      - name: Install deps
        run: |
          apt-get update
          apt-get install -y --no-install-recommends \
              ca-certificates curl

      - name: Install mdBook
        run: |
          MDBOOK_VERSION=0.4.52
          url="https://github.com/rust-lang/mdBook/releases/download/v${MDBOOK_VERSION}/mdbook-v${MDBOOK_VERSION}-x86_64-unknown-linux-gnu.tar.gz"
          curl -sSL "$url" | tar -xz -C /usr/local/bin
          mdbook --version

      - name: Build site with mdBook
        run: |
          mdbook build
          test -f doc-site/index.html

      - name: Publish to Codeberg Pages
        uses: https://codeberg.org/git-pages/action@v2
        with:
          site: https://gregburd.codeberg.page/pg_tre/
          token: ${{ forge.token }}
          source: doc-site/
