• v1.6.0 fcc67c64fd

    v1.6.0
    All checks were successful
    CI / changelog (push) Successful in 22s
    CI / test (push) Successful in 23s
    Release / release (push) Successful in 33s
    Stable

    mikluko released this 2026-10-31 18:57:37 +00:00 | 1 commits to main since this release

    Added

    • append action, mikluko/action-changelog/append@v1, which adds a note
      under a section of the [Unreleased] entry.
    • release action, mikluko/action-changelog/release@v1, which turns the
      [Unreleased] entry into a dated or open versioned entry, with the version
      given or computed from its notes, and reports version, notes and
      prerelease.
    • append and release subcommands of the command.
    • The Go packages sarak.sh/actions/changelog/pkg/changelog,
      sarak.sh/actions/changelog/pkg/semver and
      sarak.sh/actions/changelog/pkg/git.

    Changed

    • The command's module path is sarak.sh/actions/changelog, run as
      go run sarak.sh/actions/changelog/cmd/changelog@latest.
      github.com/mikluko/action-changelog gets no further versions.
    Downloads
  • v1.5.0 649b29c7f3

    v1.5.0
    All checks were successful
    CI / test (push) Successful in 51s
    CI / changelog (push) Successful in 53s
    Release / release (push) Successful in 16s
    Stable

    agent released this 2026-10-22 10:11:28 +00:00 | 3 commits to main since this release

    Changed

    • A composite action that downloads a prebuilt static binary from the
      release's assets, verifies it against the release's checksums.txt and runs
      it, rather than a Docker container action. It runs on Linux and macOS runners,
      on X64 and ARM64, and needs no Docker. Inputs, outputs and behavior are
      unchanged. No image is published for this release or later ones; the tags
      before it keep theirs.
    • Developed at https://forge.sarak.sh/actions/changelog, which Forgejo
      workflows reference as https://forge.sarak.sh/actions/changelog@v1.
      mikluko/action-changelog on GitHub is its mirror and carries every release,
      so a GitHub workflow referencing mikluko/action-changelog@v1 resolves it as
      before.
    Downloads
  • v1.4.0 26a856bebe

    v1.4.0 Stable

    github-actions[bot] released this 2026-10-04 14:51:58 +00:00 | 6 commits to main since this release

    Added

    • due and due-reason outputs. due is true when the newest entry
      names a version, no tag names it, and it is not a pre-release; due-reason is
      one sentence for the false case, also written to the step summary.
    • due-prerelease input, default false, which lets a pre-release count as
      due.
    Downloads
  • v1.3.1 6dc550835d

    v1.3.1 Stable

    github-actions[bot] released this 2026-09-10 20:43:28 +00:00 | 11 commits to main since this release

    Fixed

    • duplicate-section says nothing about an entry the reference tag has
      frozen.
      Shipped at error in 1.3.0, it fired on released entries whose
      repair release-entry-modified forbids, and between them the two checks
      described a document that cannot exist: removing the duplicate rewrites notes
      a tag published, and leaving it keeps the run red. A repository carrying one
      anywhere in its history had no move that turned the run green.

      The set spared is the one immutable already refuses to let anyone edit, so
      the two agree by construction rather than by a rule stated twice and kept in
      step by hand. An entry the tag never carried is checked as before:
      backfilling history nobody recorded is a repair, and a repair may be edited.

    Downloads
  • v1.3.0 fca6d21f43

    v1.3.0 Stable

    github-actions[bot] released this 2026-09-10 17:42:24 +00:00 | 14 commits to main since this release

    Added

    • duplicate-section, for a level-3 heading repeating a name another section
      under the same entry already carries
      , at error. unknown-section holds
      each heading to the vocabulary but never compares one to another, so two
      ### Added headings under one entry parsed clean, each with its own list
      below it. That is the shape a rebase leaves where it replays a bullet into an
      entry already carrying the section it belongs under. Nineteen checks.
    Downloads
  • v1.2.0 d30e5ab9e9

    v1.2.0 Stable

    github-actions[bot] released this 2026-09-07 07:28:16 +00:00 | 19 commits to main since this release

    Added

    • unreadable-version, a check of its own for an entry heading naming no
      version that can be read
      , at error, which leaves heading-form the shape
      question its name is about. One check was doing both jobs and only one of them
      is a policy: heading-form is about the form of a heading, which a repository
      may reasonably differ on and switch off, and switching it off also switched
      off the only thing standing between a heading nothing can read and a release
      described from the wrong entry. It is the split undated-entry and
      undated-release made, for the same reason. Sixteen checks.

    • oci-incompatible-version, for a version that is valid Semantic Versioning
      and cannot be an OCI tag
      , at error and switchable off. The specification
      permits 1.2.3+build.1; the OCI distribution specification's tag grammar is
      [a-zA-Z0-9_][a-zA-Z0-9._-]{0,127}, which has no +. Git ref syntax does
      permit it, so such a version cuts a tag and is then not a name a registry
      takes. The finding names the character at fault, and the length bound is
      checked too: a pre-release run has no bound in SemVer and a tag stops at 128.

      This is the first check whose subject is outside the document. The other
      seventeen are about the changelog or about the repository's tags; this one is
      about what a version can become somewhere else, and the register says so
      rather than pretending otherwise. A repository that publishes no image or
      chart switches it off in off: and it never fires again. Eighteen checks.

    • A SemVer compliance section in the README, stating what this action
      accepts as a version and what four named tools do with one: x/mod/semver's
      two documented deviations, Masterminds/semver/v3 admitting a pre-release
      only where the constraint's own literal carries one, the difference between
      matching a range and selecting from it, and the OCI tag grammar having no +.
      Each is documented where its own tool lives and nowhere near the
      specification, so a reader holding a valid version had no way to meet them
      except by running into one. Every entry names the version it was read against.

    • date-mismatch, for a released entry whose date is not the day its tag was
      cut
      , at error. Nothing compared the two: date-format reads the shape,
      date-order and date-future read the document alone, and the immutability
      pair compares text. An entry could claim 2026-09-01 while v1.2.3 was cut
      on the 11th and every check passed. An entry no tag names is silent, because a
      release pending is not a date that disagrees. Seventeen checks.

      Two things it has to get right, and both are settled where the tag is read
      rather than where the dates are compared. A repository cuts one kind of tag
      or the other
      , and they carry their date in different places: an annotated
      tag has a tagger date of its own, a lightweight tag is a bare reference whose
      only date is on the commit it names. And the day is the tag's own, not
      UTC
      — a changelog date means the day the human cut the release, so
      normalising a tag cut at 23:30-07:00 turns a correct entry into a finding.

      The open entry is what makes this check exact. Under release-branch the
      date is written by the merge that cuts the tag, so it is a record. Under
      release-trunk it is written in the pull request before the tag exists, so it
      is a prediction, and a pull request open across midnight ships an entry that
      disagrees with its own tag legitimately. That is the invocation that switches
      this check off, the way a branch accumulating a release switches off
      undated-entry.

    Changed

    • What counts as a version is Semantic Versioning 2.0.0 exactly, read by a
      parser written here rather than by golang.org/x/mod/semver, whose own package
      doc declares two deviations from the specification: it requires a leading v,
      and it recognises vMAJOR and vMAJOR.MINOR as alternatives to the
      three-component form. Working around those cost a canonicalisation comparison
      that carried a second policy nobody chose. Two headings change verdict:
      [v1.2.3] named a version and now names none, because the leading v is
      x/mod's requirement and a heading is not a tag; and [1.2.3+build.1] named
      none and now names one, because build metadata is valid Semantic Versioning.
      Whether a version this project accepts is one it wants is a separate
      question, asked under a name of its own. One departure is this project's and
      not the specification's: a major, minor or patch component past 2^64-1 is
      rejected, where the specification sets no bound.

    • golang.org/x/mod/semver is gone, and ordering is this project's too.
      Sections 10 and 11 are where the specification stops being obvious: build
      metadata is ignored for precedence, so 1.0.0+a and 1.0.0+b are one version
      and two strings; a numeric pre-release identifier ranks below every identifier
      that is not one; and numeric identifiers compare numerically with no upper
      bound, which 1.0.0-99999999999999999999 needs and no fixed-width integer
      gives. They carry no leading zero, so the longer of two is the larger and the
      value is never read as a number.

    • A tag is read by ParseTag, which is deliberately laxer than the
      specification and says so.
      A tag namespace carries a leading v, and the
      GitHub Actions convention keeps a moving v1 beside the v1.0.0 it points
      at; both name one version, and the fuller spelling still wins the tie for the
      reference tag. That behaviour used to rest on x/mod accepting vMAJOR
      shorthand as a side effect. It is now a named, tested contract of this
      project's own, and read strictly it would have gone silently.

    • A tag now matches an entry by precedence rather than by string equality, so an
      entry naming build metadata matches the tag naming its version. It could not
      before, because canonicalisation dropped the metadata from one side only.

    Fixed

    • undated-release no longer fires where the tag naming the newest entry is a
      candidate.
      The check split on whether a tag already named the entry's
      version, read as the release shipped. That reading is sound for a final and
      false for a pre-release: a candidate ships its tag and stays undated on
      purpose, which is what a stabilization branch is for. It fires on a final
      tag now, and a candidate falls through to undated-entry, which is the check
      such a branch already switches off.

    • The defect was unreachable until the release-branch example stopped
      composing its tag. While the branch cut vX.Y.Z-pre.<run> and the entry named
      X.Y.Z, no tag a branch cut ever named its own entry's version. Once the
      entry names what is cut, the tag names the entry's version from the first
      candidate on, and every push after it was a finding on the one branch whose
      purpose is to cut them.

    • The release-branch example teaches what the strategy's own adopter
      settled on: the changelog names the release it is heading for and never an
      attempt at one.
      The entry says 1.3.0 from the moment the branch opens; each
      pull request into that branch cuts a numbered candidate, v1.3.0-rc.1 and up;
      merging to the trunk dates the entry and cuts v1.3.0. A heading therefore
      never carries an identifier, and prerelease-entry is raised on every
      invocation rather than on the trunk alone.

    • A release branch has no push trigger. A candidate comes from a pull request
      into the branch, which is what lets the suite stand in front of it: the cut is
      a job with needs: [changelog, suite], and needs: is job-level, so a step
      had no boundary a gate could attach to. The two cutting workflows also carry
      paths: [CHANGELOG.md], so a change proposing no release starts no run at all.

    • The workflows are named for what each run yields, not for what triggers it:
      ci, release-candidate, release, publish, and the suite they call.
      Read that list and it is the strategy. It replaces pull-request.yaml,
      release-branch.yaml and main.yaml, whose names described their triggers and,
      in one case, described the wrong thing.

    • Where the newest entry's heading names no version that can be read, version
      and notes report nothing rather than describing the entry below it. That
      entry was being read as the newest, so a workflow reading those cut a tag for
      the wrong release and published the previous release's notes under it. Nothing
      under such a heading is the newest entry, and reporting nothing is what a
      document naming no version already does, so a workflow guarding on an empty
      version lands on the path it already has. already-tagged and prerelease
      answer false beside it, as they do for a document naming no version.

    • A rejected heading names the rule it broke rather than restating the
      grammar. One message covered every rejection, so the author of
      ## [1.3.0+build.1] was told that a version states all three of major, minor
      and patch, which it does. A leading zero, a component that is not a number, an
      empty identifier, a character outside [0-9A-Za-z-] and a leading v now each
      say so, and point at the fragment at fault.

    Downloads
  • v1.1.0 b57fd61d74

    v1.1.0 Stable

    github-actions[bot] released this 2026-09-05 14:37:49 +00:00 | 56 commits to main since this release

    Added

    • undated-entry and undated-release, a pair the repository's tags decide
      between
      where the newest entry names a version and carries no date. No tag
      names that version yet, and the entry is open: a release still accumulating on
      a branch of its own, which names its version before its date is known.
      undated-entry reports it, defaulting to error because an entry with no
      date is illegal under Keep a Changelog, and a branch accumulating a release
      is the one invocation that switches it off
      in off:, the way a trunk
      invocation switches prerelease-entry on. A tag already names that version,
      and the release shipped and nobody dated it: undated-release reports that,
      also at error, and switching the other check off does not silence it.
      Fifteen checks.
    • The two are separate because one setting cannot serve both. A stabilization
      branch has to admit its open entry on every run, and a release that reached a
      tag with no date is a defect on exactly that workflow, so a single check would
      be switched off for the whole of the window in which the defect occurs. The
      same tag question answers both, and it is the one already-tagged reports, so
      the check and the output cannot disagree about which case a run is in.
    • Where the tag history cannot be read, the answer is unknowable and the entry
      is reported as open. no-git-tags already names that cause, and accusing a
      repository of shipping an undated release on the strength of tags nobody could
      read is the more expensive of the two mistakes.

    Changed

    • heading-form accepts ## [1.2.3] with no date on the newest versioned entry
      only, which is the entry the two checks above then answer for. Every entry
      below it has shipped and carries a date, so one missing there still fires
      heading-form.
    Downloads
  • v1.0.2 5531c2b639

    v1.0.2 Stable

    github-actions[bot] released this 2026-09-04 14:04:40 +00:00 | 62 commits to main since this release

    Fixed

    • Where two tags name one version, the reference tag is the one that spells it
      most fully. A repository following the GitHub Actions convention carries v1
      beside v1.0.0, both read as v1.0.0, and which of them won was whatever
      order the refs happened to arrive in. latest-tag could therefore report the
      moving major tag, which is documented as the ref a workflow can check out and
      is the one that moves to the next release under whoever checked it out.
    Downloads
  • v1.0.1 819f02e504

    v1.0.1 Stable

    github-actions[bot] released this 2026-09-04 13:58:04 +00:00 | 64 commits to main since this release

    Fixed

    • no-git-tags fires where the repository is present and cannot be read. A
      .git file naming a git directory that does not resolve here (a linked
      worktree away from its parent, a submodule without the superproject, a
      container that mounted the working tree and not the repository) read as a
      repository holding no tags, so every tag-dependent check passed on a history
      nothing had read. The finding's message now carries the remedy for both
      causes and names the one this run met.
    Downloads
  • v1.0.0 2862526168

    v1.0.0 Stable

    github-actions[bot] released this 2026-09-04 13:33:05 +00:00 | 67 commits to main since this release

    First release. The candidates that preceded it were staging posts and are not
    recorded separately: nothing consumed them but this repository.

    Added

    • A GitHub Action, and a command, that reads a Keep a Changelog document and
      reports where it departs from the format. Each finding lands as a workflow
      annotation on the offending line of the diff. Run with no argument it
      validates CHANGELOG.md.
    • Thirteen checks, each with a name that appears in the finding, in the
      annotation's title, and in the flags that reconfigure it. -list-checks
      prints the register, and the README's table is generated from that same
      register with CI failing on a stale copy, so the documentation cannot
      disagree with the binary.
    • Severity is the practitioner's, not the tool's. -error, -warn and
      -off move a named check, and -fail-on error|warning|never decides what
      turns the step red. A check that catches a defect is an error by default; a
      check that encodes a policy is off by default, because a policy check's
      absence is the majority case being correct.
    • Six outputs, written to $GITHUB_OUTPUT and printed on stdout for a local
      run: valid, version, notes, already-tagged, prerelease and
      latest-tag. Every one is something the action read, never a convention it
      chose: nothing proposes how to spell a tag, because that belongs to whatever
      cuts them. notes is the entry body verbatim, under a delimiter drawn at
      random per value so that a changelog cannot declare outputs of its own.
    • One reference tag behind every tag-dependent check and behind
      latest-tag: the newest version tag reachable from HEAD, which is what
      git describe --tags names. Reachability takes no setting, because a tag on a
      branch the checkout is not on is never the right baseline, and it is what lets
      a maintained support line compare against its own last release rather than
      another line's. reference-tags: final|all decides whether a pre-release may
      serve as one, defaulting to final. A repository that tags no pre-release
      reads the same under either setting.
    • Checks that read the repository as well as the document: no-git-tags for a
      checkout whose tag history cannot be read, version-behind-tag for a newest
      entry behind the reference tag, and release-entry-modified and
      prerelease-entry-modified for a released entry that has changed or gone
      since it. A released entry is immutable; an entry added for a version already
      tagged is not, because recording history nobody wrote down is a repair rather
      than a rewrite.
    • A Docker container action on a scratch base, published for linux/amd64 and
      linux/arm64. It reads git through go-git rather than by shelling out, so the
      image carries the binary and nothing else.
    • examples/ carries one worked policy: a changelog written to it,
      the two workflow invocations that enforce it, and a deliberately broken copy.
      A test resolves the inputs out of those workflow files and runs the linter
      under them, so the example is executed rather than described.
    • This repository releases itself with its own action. A push to main runs
      the action against this changelog and, where it names a version no tag
      carries, publishes the image, cuts the tag and creates the release from
      notes. Release candidates are cut from the pull request instead, so a
      candidate can be pulled and tested before it merges, and one reaching main
      is refused rather than published.
    Downloads