[Pre-RFC] Package changelog

Hi,

To my knowledge the last attempt to get changelog in crates is RFC: Crate changelogs by newpavlov · Pull Request #2129 · rust-lang/rfcs · GitHub, which was refused.

This proposal attempts to fix some of the issues that were identified with the proposal at the time and is based on it, however I'm not very familiar with cargo and might have blundered on the way, in which case please let me know here or as a PR comment here: Create proposal to add changelog attribute support to cargo by Ayowel · Pull Request #1 · Ayowel/rustlang_rfcs · GitHub.

Thank you,

I appreciate the work you've put into this. Can you clarify what you've changed since the last version of RFC 2129?

While it seems like some changes have taken place, I'm not clear on how this addresses the primary concerns raised when closing RFC 2129: we don't want to prescribe a changelog format, and if we're not going to prescribe format then this doesn't add much value over just putting a link in your README.md.

As far as I can tell, the main value of specifying a format is that cargo can check if the version number is up to date. However, something similar could be part of cargo release or cargo smart-release or similar tools. Or, if we ever added a publish.rs hook, that could do the same check (likely using a helper crate to avoid reimplementation).

Simply put, more lax constraints on the changelog's markdown and automatic inclusion if a remote file is specified, as well as some options that are not really relevant considering the main issue.

It seems i did not properly understand the reason for the rejection. Based on this comment RFC: Crate changelogs by newpavlov · Pull Request #2129 · rust-lang/rfcs · GitHub , I thought that the text's structure constraints provided in the example were what was 'too strict', not the use of a specific format such as markdown.

Allowing any format to be used would trigger a whole range of issues if it were to be displayed on the crate's web page (such as code injections if html is used as-is). That said, I doubt printing the file as a raw text file would be a satisfactory solution. Could a set of supported file types (adoc/md/txt for example) without pre-validation be acceptable ?

I believe Josh is using format not to mean "markdown vs asciidoc vs txt" but how the changelog is written inside the markdown.

I don't think this is possible beyond "markdown" and "put the version in section titles". Which I think is fine. I don't think I'd agree about much else (as I'm 1000% against anything doing clog-like autogeneration).

This is slightly more requirements than we have on the readme = Cargo link (which is de facto Markdown due to the assumptions I think "everything" uses when rendering it AFAIK).

There's one big one: the file is properly version controlled. Any readme link needs updated on each release with some permalink or becomes outdated as new releases are made.

I would expect the changelog link to remain the same from version to version, and only the contents would change.

1 Like

So after $time, trying to find the changelog for v0.4 involves following a link and hoping you can find it there (pagination woes would be the main worry). I think a permalink is far better (especially if something tries to be smart and use the content at the link to render it directly).

May contain (sub-)headlines without a version when --allow-unversionned-changelog-head

To me this is too opinionated, and leans on options too much. Crates may want to have "Added"/"Fixed" subsections, and having to add a long attribute every time would be like --hey-cargo-stop-being-annoying flag.

In terms of machine-readability, it's easy enough to just split the document on headings that contain a version. This automatically ignores any intro before the first version heading, and automatically includes any subheadings within each section's version. And that's fine.

Most release metadata is tracked elsewhere, so the changelog only needs to contain human-readable information.

So I suggest only checking if any markdown heading contains the latest version when publishing.

2 Likes

changelog = "https://github.com/foo/bar/releases"

Could it be limited to local files, just like README.md? Most projects track their changelog files in the same repo. Having a URL adds more moving parts to Cargo, and creates a chicken-egg problem for tools like cargo-release that publish before tagging and pushing the repo.

3 Likes

Not to say that which is a better scheme[1], but this is fairly simply resolvable by the tooling. The cargo-release pipeline could be

  • assert git status is clean
  • Version increment things + git commit
  • cargo publish --dry-run
  • success
    • git tag "v$CARGO_PKG_VERSION"
    • git push --tag "v$CARGO_PKG_VERSION"
    • gh release "v$CARGO_PKG_VERSION"
    • cargo publish --no-verify
  • failure
    • git reset --hard HEAD~1

i.e. verify a publish will work, push release info, do actual publish.

Also, the tag already ideally should exist when doing the cargo publish, so the publish can note the tag as well as the sha.


  1. I can't resist saying that a packaged changelog avoids the versioned link desire/problem, though. ↩︎

You cannot verify whether a publish will work locally, crates.io has extra checks that will reject some successful packages (IIRC cargo allows you to use :: in features but crates.io doesn’t, which broke one of my releases).

In theory, crates-io could (perhaps should) provide an idempotent “would this write succeed” API. Obviously it'd still be liable to TOCTOU style failures, but if you're already the author of a package you should be able to rule that out.

And also of course saying an API should/could exist is no good to implementations if it doesn't :slightly_smiling_face: but at least GitHub tag/release URLs are equally as predictable, so it could go the other way.

(Plus, tooling can use the CLI (or the API) to automate publishing the release as well; and if I've skimmed the manual(s) correctly, GH releases can have a staged draft which is then made public after confirming the actual publish.

Deleting a remote tag is not the most desirable thing, but still possible.)

Side note: please use -a to annotate tags and add a description (usually crate-name X.Y in my usage). This works much better with git describe (in that if there are annotated tags, non-annotated tags will not be considered by default). Note that some workspaces may have different version numbers for different directories, so crate-name/vX.Y may be a better tag name in some situations.

This is exactly the kind of checking I implemented in my little version-sync crate

https://crates.io/crates/version-sync

It's just a few utility functions which people can use to easily implement whatever policy they want for their crates. I'm using it in all of my own crates such as text wrap. The check for an updated changelog is simply

#[test]
fn test_changelog() {
    version_sync::assert_contains_regex!(
        "CHANGELOG.md",
        r"^## Version {version} \(20\d\d-\d\d-\d\d\)"
    );
}
1 Like

RE Motivation

So the original RFC's motivation was to encourage changelogs and the only affect was on crates.io to link to it.

Josh brought up version verification.

Other random ideas I had:

  • Showing the changes inline with the versions
  • Showing the changes with cargo update or cargo upgrade for direct dependencies
    • I've found that clap users blindly upgrade between breaking releases and then playing whack-a-mole with the compiler without looking into what breaking changes they might miss with the compiler and their tests
    • This has the potential of being too noisy though

RE Changelog format

One thought I just had is we could have a changelog.rs that had access to dev-dependencies to extract a changelog fragment. This would implicitly be a test target to verify it works. We'd then run it on cargo publish to extract the changelog fragment needed for the published version. This would open us up to whatever format the user wanted with shared crates for parsing common formats. We might even be able to have tools like clog plug directly into this and generate the information dynamically.

This sounds about the worst of all worlds. I would really want to be some verification step of "does this changelog look ok?". Then you get into the case of "what to do if not?". And that's besides my feeling that clog is just a misguided project/effort in the first place[1].

[1] Main reasons:

  • Reverts exist: how does one remove something from the changelog.
  • If a feature gets changed, how do I update its release note?
  • How do typos get fixed after the fact?
  • I also don't like conventional commit formatting because the "type" is way too subjective and takes up precious commit subject space.

Details about clog are beside the point. I was trying to demonstrate that this would allow exploration and evolving policy. You might feel that the trade off on changelog generation leans against doing it but we should consider whether we should be prescriptive against it because the trade off might be different for other people.

Frankly, I also suspect it'll be too much overhead unless we offer changelog.rs-as-a-dependency somewhat like there had been talk of that for build.rs.

btw another example, I've been wanting to explore a changelog made up of fragments per change so entries can be included in the commit that adds them without having merge conflicts.

ps the reason I've been moving away from automated changelog generation and automated version selection is multi-crate repos could have a change span multiple crates but only require a major or minor release for one of those crates.

My main concern is about no review between autogeneration and publishing. If there's a "please verify changelog" step, how it gets made is of little consequence to me (I just don't know how auto-generated ones are going to fix their problems without just doing things manually anyways).

This is what I push for on larger projects. The release process then includes a step where the notes get collated into a single document with proper sorting and such. It also allows for refinements to features to update notes as required.

It's how CMake does its release notes (3.24 just branched a few weeks ago, so there aren't many in there right now).

Ah, yes, that's another reason :slight_smile: .