Skip to content

Updaters rewrite a reference that sits inside a comment #308

Description

@fniessink

The problem

Every line-based updater matches its regexp against a line's whole text, and none of them knows where the line's
comment begins. A reference someone commented out therefore reads to Update-time exactly like a live one: it is
resolved, a request is spent on it, and it is rewritten inside the comment.

Probed 2026-08-28, matching each updater's own regexp against a line of the kind of file it scans:

YAML image, commented out: MATCHES 'image: redis:6.2'
devcontainer image in a // comment: MATCHES '"image": "mcr.microsoft.com/devcontainers/base:1.0"'
devcontainer feature in a // comment: MATCHES '"ghcr.io/devcontainers/features/docker-in-docker:2": {'
action uses: in a comment: MATCHES 'uses: actions/checkout@v4'
pre-commit repo: in a comment: MATCHES 'repo: https://github.com/astral-sh/ruff-pre-commit'
jsDelivr URL in a # comment: MATCHES 'https://cdn.jsdelivr.net/npm/clipboard@2.0.11/dist/clipboard.min.js'

A commented-out line is everyday practice: the image a Compose file ran before an upgrade, the alternatives a
devcontainer template ships, a workflow step dropped for now.

The pre-commit config is the serious one

There the mistake does not stay in the comment. _updated_lines reads the repository from any line _REPO_RE
matches and applies it to every rev: below it (update_pre_commit_config.py:70-77), so a comment naming another
project silently re-points a live hook:

repos:
  - repo: https://github.com/psf/black
    # We also considered repo: https://github.com/astral-sh/ruff-pre-commit
    rev: v24.1.0

Black's rev: is then resolved against astral-sh/ruff-pre-commit and pinned to that project's commit. Nothing
about the result looks wrong to a reader, and the log names the wrong repository.

Stripping the comment is not the fix

Two updaters read a version out of a trailing comment. Probed the same day:

a pinned action, version in its trailing comment: matches 'uses: actions/checkout@<sha> # v4.1.1'
    version read from the comment: '4.1.1'
a frozen rev, version in its trailing comment: matches 'rev: <sha>  # frozen: v4.5.0'
    version read from the comment: 'v4.5.0'

An updater that matched only the code part of a line would stop reading every pinned action and every frozen
rev: in the repository.

The rule that holds is narrower: a reference may not start inside a comment. What follows the reference stays
readable, so the trailing version comments keep working.

Scope

  • Say where a line's comment begins: a comment lead that opens the line or follows whitespace, outside quotes. The
    leads are the ones domain/marker.py already spells, so they are named once. Quoting matters: a repo: value
    holds https://, which is not a comment.
  • Reject a match that starts at or after that point, in the three places that match a line: the shared engine's
    _reference_match, the pre-commit pass, and the jsDelivr pass. The last two walk their own lines.
  • Leave marker reading alone. An # update-time: marker is a comment, and parse_marker goes on reading the
    whole line.

Out of scope

Four defects the same audit found, each with a cause of its own:

  • YAML_IMAGE_REFERENCE matches any key ending in image, so init_image: busybox:1.36 is taken for an image
    reference (sources/oci.py:69).
  • _ATTRIBUTES_RE gives a resolved integrity hash to the next line holding a {, so a bare-string jsDelivr URL
    writes "integrity" into whatever dictionary follows it (updaters/update_jsdelivr.py:44).
  • _IMAGE_RE matches a line-initial FROM inside a BuildKit heredoc body, so embedded SQL is looked up as an
    image (updaters/update_dockerfile_base_image.py:21).
  • _STAGE_NAME_RE is greedy and case-insensitive, so a trailing comment's "as" is captured as the stage name and
    the real stage goes unregistered (updaters/update_dockerfile_base_image.py:27).

A uses: inside a run: shell string is out of scope too. It sits in a string rather than a comment, so the rule
above does not reach it.

Documentation

  • A changelog entry: a project with a commented-out reference has been having it rewritten, so the fix is
    user-visible.
  • No README change. The README never says what happens to a commented-out reference, and the fix makes it do the
    thing a reader would assume.

Open questions

  1. Does the comment offset belong on Line, which already carries the text a marker is read from, or in
    primitives/text.py beside line_number and column?
  2. How far does the quote tracking need to go? A YAML single-quoted scalar and a Python string are the cases the
    scanned files hold; a Python triple-quoted string spans lines, which a line-based pass cannot see.
  3. Should a marker inside a commented-out reference's own line still be read, as in
    # image: redis:6.2 # update-time: ignore? The reference is not updated either way, so the question is only
    whether such a marker is reported as recognised.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions