default: check

lint:
    uv run ruff check .
    uv run ruff format --check .

fmt:
    uv run ruff format .

fix:
    uv run ruff check --fix .
    uv run ruff format .

type:
    uv run ty check src tests

test:
    uv run pytest tests/unit -q

cov:
    uv run pytest tests/unit --cov --cov-report=term
    uv run coverage report --fail-under=80

cov-html:
    uv run pytest tests/unit --cov --cov-report=html
    uv run coverage report --fail-under=80

slow:
    uv run pytest tests/unit -m slow -v

integration:
    uv run pytest tests/integration -q -m "integration and not sql_endpoint"

build:
    uv build

# Shared calver validator — used by both 'release' and 'tag'.
# Accepts YYYY.M.N where year is 20xx, month is 1–12 (no leading zero), patch is 0 or
# any positive integer without a leading zero.  Rejects prerelease suffixes (aN/bN/rcN/.devN).
# Single source of truth: update this regex in one place only.
_CALVER_RE := '^20[0-9]{2}\.([1-9]|1[0-2])\.(0|[1-9][0-9]*)$'

# Bump plugin.json (and its Copilot CLI mirror) to VERSION (must be a stable calver
# like 2026.6.1; no prerelease suffixes).
# Run: just release VERSION  →  open a release-prep PR  →  merge  →  just tag VERSION
release VERSION:
    #!/usr/bin/env bash
    set -euo pipefail
    if ! echo "{{ VERSION }}" | grep -qE '{{ _CALVER_RE }}'; then
        echo "error: '{{ VERSION }}' is not a stable calver (expected YYYY.M.N, month 1–12, no leading zeros, no prerelease suffix)" >&2
        exit 1
    fi
    # plugins/fabric-dw/.github/plugin/plugin.json is the GitHub Copilot CLI mirror of
    # plugins/fabric-dw/.claude-plugin/plugin.json (see publish.yml's sync-plugin-manifest
    # job) and must move in lockstep — both carry a "version" field. The root marketplace.json
    # files intentionally omit "version" so need no bump.
    plugin_jsons=(
        "plugins/fabric-dw/.claude-plugin/plugin.json"
        "plugins/fabric-dw/.github/plugin/plugin.json"
    )
    for plugin_json in "${plugin_jsons[@]}"; do
        # Replace only the version value; keep key order, indentation, and trailing newline byte-identical.
        # sed -i '' on macOS; sed -i on Linux — detect via uname.
        if [ "$(uname)" = "Darwin" ]; then
            sed -i '' 's/"version": "[^"]*"/"version": "{{ VERSION }}"/' "$plugin_json"
        else
            sed -i 's/"version": "[^"]*"/"version": "{{ VERSION }}"/' "$plugin_json"
        fi
    done
    echo "plugin.json and its Copilot CLI mirror set to {{ VERSION }}"
    echo ""
    echo "Next steps:"
    echo "  1. git add plugins/fabric-dw/.claude-plugin/plugin.json plugins/fabric-dw/.github/plugin/plugin.json && git commit -m 'chore: release {{ VERSION }}'"
    echo "  2. Open a release-prep PR and merge it to main."
    echo "  3. After merge: just tag {{ VERSION }}"

# Create and push an annotated git tag for VERSION.
# Refuses to tag on a dirty working tree, a non-main branch, or when the branch is behind
# origin/main.  plugin.json does NOT need to be pre-bumped: the Publish CI workflow
# auto-bumps it on main after the tag fires (see sync-plugin-manifest job in publish.yml).
# 'just release VERSION' remains available if you want to pre-bump locally.
tag VERSION:
    #!/usr/bin/env bash
    set -euo pipefail
    # Validate VERSION with the same tightened calver check used by 'release'.
    # Also reject any embedded newlines (multi-line input can fool line-anchored grep).
    case "{{ VERSION }}" in
        *$'\n'*) echo "error: VERSION contains a newline — must be a single-line value" >&2; exit 1 ;;
    esac
    if ! echo "{{ VERSION }}" | grep -qE '{{ _CALVER_RE }}'; then
        echo "error: '{{ VERSION }}' is not a stable calver (expected YYYY.M.N, month 1–12, no leading zeros, no prerelease suffix)" >&2
        exit 1
    fi
    # Guard: working tree must be clean (tracked and untracked files).
    if [ -n "$(git status --porcelain)" ]; then
        echo "error: working tree is dirty — commit or stash changes before tagging" >&2
        exit 1
    fi
    # Guard: must be on main.
    current_branch=$(git rev-parse --abbrev-ref HEAD)
    if [ "$current_branch" != "main" ]; then
        echo "error: must be on 'main' to tag (currently on '$current_branch')" >&2
        exit 1
    fi
    # Guard: branch must be up to date with origin/main.
    # Compare HEAD against FETCH_HEAD so the check always uses the freshly fetched SHA,
    # regardless of whether the local remote-tracking ref (origin/main) was up to date.
    git fetch origin main --quiet
    local_sha=$(git rev-parse HEAD)
    remote_sha=$(cat "$(git rev-parse --git-dir)/FETCH_HEAD" | awk '{print $1; exit}')
    if [ "$local_sha" != "$remote_sha" ]; then
        echo "error: local main is not up to date with origin/main — run 'git pull' first" >&2
        exit 1
    fi
    # Informational: log current plugin.json version (mismatch is not an error).
    plugin_version=$(git show HEAD:plugins/fabric-dw/.claude-plugin/plugin.json | python3 -c "import json,sys; print(json.load(sys.stdin)['version'])")
    if [ "$plugin_version" != "{{ VERSION }}" ]; then
        echo "Note: plugin.json is at '$plugin_version'; CI will auto-bump it to '{{ VERSION }}' after the tag fires."
    fi
    git tag -a "v{{ VERSION }}" -m "Release v{{ VERSION }}"
    git push origin "v{{ VERSION }}"
    echo "Tagged and pushed v{{ VERSION }}"

audit:
    uvx 'bandit[sarif]==1.9.4' -r src/ -ll
    uvx 'pip-audit==2.10.1' --strict

check: lint type test
