Source code for tests.test_self_pin

# Copyright Kevin Deldycke <[email protected]> and contributors.
#
# 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 2
# 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, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

"""Tests for the spellings of the upstream toolkit's pin on itself."""

from __future__ import annotations

from datetime import date
from pathlib import Path

import pytest

from repomatic.config import Config
from repomatic.deps.sync import ResolveContext, _resolve_workflow_pins
from repomatic.lint_repo.context import LintContext
from repomatic.lint_repo.pins import (
    check_inline_pins_match_upstream,
    check_self_pin_cooldown_exemption,
)
from repomatic.release.self_pin import (
    add_cooldown_exemption,
    align_inline_pins,
    cooldown_exemption,
    cooldown_exemptions,
    find_inline_pins,
    frozen_cli_invocation,
    frozen_cli_re,
    local_cli_invocation,
)
from repomatic.upstream import DEFAULT_REPO, UPSTREAM_PACKAGE

UPSTREAM_REF = (
    "jobs:\n"
    "  lint:\n"
    f"    uses: {DEFAULT_REPO}/.github/workflows/lint.yaml"
    "@1234567890abcdef1234567890abcdef12345678 # v6.29.0\n"
)
"""A SHA-pinned thin-caller ref to the upstream toolkit, declaring `v6.29.0`."""

PIN_SHAPES = (
    pytest.param("uvx --no-progress 'repomatic=={}' show-metadata", id="quoted-uvx"),
    pytest.param("uvx repomatic=={} show-metadata", id="bare-uvx"),
    pytest.param("uv tool install 'repomatic=={}'", id="tool-install"),
    pytest.param("uvx --with 'repomatic[cli]=={}' papaya", id="with-extras"),
    pytest.param("pip install repomatic=={}", id="pip-install"),
)
"""Every shape a workflow writes the self-pin in, with a `{}` for the version."""


def _lint_context(text: str) -> LintContext:
    """A lint context holding one workflow file, *text*, and none on disk."""
    ctx = LintContext()
    ctx.__dict__["workflow_texts"] = {Path(".github/workflows/ci.yaml"): text}
    return ctx


[docs] @pytest.mark.parametrize("shape", PIN_SHAPES) def test_sync_fixes_every_pin_the_lint_flags(shape: str) -> None: """`sync-workflow-pins` rewrites each pin shape the lockstep check reads. A shape the check reads and the rewrite skips leaves `lint-repo` red until a human edits the workflow, with no pull request to say why. """ text = UPSTREAM_REF + f" - run: {shape.format('6.28.1')}\n" assert check_inline_pins_match_upstream(_lint_context(text)).passed is False rc = ResolveContext(config=Config(), today=date(2026, 10, 20)) rc.__dict__["pinnable_files"] = {Path(".github/workflows/ci.yaml"): text} plan = _resolve_workflow_pins(rc) assert ("repomatic", "6.28.1", "6.29.0") in plan.changes (written,) = plan.file_writes.values() assert check_inline_pins_match_upstream(_lint_context(written)).passed is True
[docs] @pytest.mark.parametrize("shape", PIN_SHAPES) def test_inline_pin_shapes_are_read(shape: str) -> None: """Every shape a workflow writes the pin in is one the matcher reads.""" assert find_inline_pins(shape.format("1.2.3"), "repomatic") == ["1.2.3"]
[docs] @pytest.mark.parametrize( "text", ( pytest.param("uvx 'papaya-repomatic==1.2.3'", id="longer-name"), pytest.param("--exclude-newer-package repomatic=P0D", id="exemption"), pytest.param("uvx 'repomatic>=1.2.3'", id="floor"), ), ) def test_inline_pin_matcher_ignores_other_text(text: str) -> None: """A name ending in the package, the exemption, and a floor pin nothing.""" assert find_inline_pins(text, "repomatic") == []
[docs] @pytest.mark.parametrize( ("content", "expected", "changes"), ( pytest.param( "uvx 'repomatic==1.0.0' and pip install repomatic[cli]==1.1.0", "uvx 'repomatic==2.0.0' and pip install repomatic[cli]==2.0.0", [("repomatic", "1.0.0", "2.0.0"), ("repomatic", "1.1.0", "2.0.0")], id="behind", ), pytest.param( "uvx 'repomatic==3.0.0'", "uvx 'repomatic==2.0.0'", [("repomatic", "3.0.0", "2.0.0")], id="ahead", ), pytest.param( "uvx 'repomatic==2.0.0'", "uvx 'repomatic==2.0.0'", [], id="equal" ), ), ) def test_align_inline_pins( content: str, expected: str, changes: list[tuple[str, str, str]] ) -> None: """Every pin moves onto the version, extras kept, and each move is reported.""" assert align_inline_pins(content, "repomatic", "2.0.0") == (expected, changes)
[docs] def test_cooldown_exemption_names_its_package() -> None: """A fork under another name gets an exemption for its own package.""" assert cooldown_exemption("fig") == "--exclude-newer-package fig=P0D"
[docs] def test_frozen_invocation_is_what_the_unfreeze_matches() -> None: """The freeze writes one spelling, and the unfreeze recognizes it.""" frozen = frozen_cli_invocation("fig", "1.2.3") assert frozen_cli_re("fig").fullmatch(frozen) assert frozen_cli_re("fig").sub(local_cli_invocation("fig"), frozen) == ( "uv --no-progress run --frozen -- fig" )
[docs] def test_spliced_exemption_converges_on_the_frozen_spelling() -> None: """A bare `uvx` pin gains the exemption the freeze writes, and only once.""" bare = "uvx --no-progress 'fig==1.2.3' show-metadata" spliced = add_cooldown_exemption(bare, "fig") assert spliced == f"{frozen_cli_invocation('fig', '1.2.3')} show-metadata" assert add_cooldown_exemption(spliced, "fig") == spliced assert cooldown_exemptions(bare, "fig") == [False] assert cooldown_exemptions(spliced, "fig") == [True]
[docs] @pytest.mark.parametrize("exempt", (False, True)) def test_lint_reads_the_exemption_the_splice_writes(exempt: bool) -> None: """The exemption check and the splice agree on what an exempt command is.""" command = "uvx --no-progress 'repomatic==6.29.0' show-metadata" if exempt: command = add_cooldown_exemption(command, UPSTREAM_PACKAGE) ctx = _lint_context(f'env:\n UV_EXCLUDE_NEWER: "1 week"\n - run: {command}\n') assert check_self_pin_cooldown_exemption(ctx).passed is exempt