2024-09-23 | tags:
Summer is officially over now, and now so is my Google Summer of Code project for this year. This was a really great experience for me, and I'm super grateful to have had this opportunity. I had the great help of being mentored by the maintainer of the project Predrag Gruevski, which I couldn't have asked for a better mentor for this. In this blog post, I'll talk about the work I did, reflections on my experience, and what the future looks like:
the work
The title of my GSoC project that I wrote at the beginning is "adding lint-level configuration to cargo-semver-checks
." This means adding the ability for users to configure each individual check of the semantic versioning linter cargo-semver-checks
. 1 Adding this configuration granularity will let many more Rust crates adopt cargo-semver-checks
to automatically help prevent breaking the semantic versioning guarantees, which helps the whole Rust ecosystem make more fearless upgrades according to SemVer.
overrides
To configure each of cargo-semver-checks
's 90+ lints, both by lint level/severity and required SemVer update, I first added a default lint level field (#787) to each lint, then added a new API (#788) to configure overriding each lint's fields. I had to design a system that supported multiple levels of precedence, with the ability to configure lint level, required update, or both for a given lint at each of these precedence levels, so it took some careful design and adding plenty of unit tests.
the [lints]
table
After posting my initial blog post, the feedback I received from community members was very helpful. Initially, I had planned to start exposing the configuration interface as a series of CLI flags (like rustc
's --warn <name>
). In this discussion with my mentor and community members like epage, it started to look like a better idea to work on the [lints]
table in the Cargo manifest first, and reevaluate the need for CLI flags later on.
After this, I implemented the logic to read a table similar to the Cargo [lints]
table from manifests in #799, using the [package.metadata.cargo-semver-checks.lints]
(it's currently not valid for tools like cargo-semver-checks
to use the [lints]
table directly), and in #800, I implemented the logic to use the configured required update, and added tests for the many different interactions, especially with both workspace and package configuration.
After some minor fixes in #804, and #806, I finished the logic for using the configured lint level in #805. THis was much more design-intensive than using the configured required update, because this was also introducing the concept of warning-level checks to cargo-semver-checks
, and I spent a lot of time with my mentor designing how a warning should look like in cargo-semver-checks
's interface, and how warnings should interact with errors.
In #808 and #809 I added some future-proofing tests and comments to help with maintainability.
compatibility with cargo
A goal of cargo-semver-checks
as a project is to be as compatible with cargo
as possible and eventually be merged with the tool. After posting another update on cargo-semver-checks
's new configurability, part of the feedback to this was that the current lints table would not be quite compatible with cargo's. In #811, #812, and #813, I added the functionality to mimic cargo
and only read workspace configuration if a key is explicitly set in the package, and added tests (including making sure that cargo-semver-checks
denies the lints.workspace = false
key, which is insidiously ian invalid state).
documenting
At this point, the initial core configuration was ready, so I added a section to the README that was a guide on how to use this new feature in #826 and #829. I wrote a lot of documentation this summer, both developer-facing and user-facing, and it's one of the things I'm proud of. To me, documentation is often the first impression of a project, and it can make or break a user's experience, so polishing it is super important.
a detour?
Here in the project, there was kind of a lull, and I was working on polishing tests and documentation. However, one thing I was noticing was that it was kind of hard and involved to write a full integration test case. My mentor suggested that I try to improve the testing infrastructure for cargo-semver-checks
, and I thought that was a great idea. In #846, I added the foundation to be able to write integration tests using the insta
framework. I also spent some time benchmarking to figure out what kind of test (i.e., lib test
, bin test
, or, confusingly, test
binary) would be most helpful to access the right parts of cargo-semver-checks
to test while having the fastest possible compile times for better iteration and design. Here, I also wrote a lot of documentation for future contributors to have the best experience when writing new tests.
After adding this test infrastructure, I wrote some tests that pinned down cargo-semver-checks
's behavior for different edge cases, which documented this behavior, prevented any unwanted regressions, and would be easy to change if we decided in the future we wanted this behavior to be different, in #847, #859, #866, and #882. In writing these tests, I found the opportunity to polish in fix parts of the test infrastructure, like in #865, #867, #881, and #883, as well as #913 later on.
new features!
At this point, the user demand for CLI-configurable lints when there was already another avenue to configure them was not that strong, so it didn't seem like the best way forward to continue with my original proposed idea of adding CLI flags here. The project started to go off the rails a little, but in a very good way. Predrag suggested that I work on a new feature to generate witness programs, which are buildable examples of the downstream breakage of agiven breaking change, which prove that a SemVer guarantee would be broken with this change. As a fan of testing and correctness, I got to work on this feature.
Adding witnesses is a big feature, so we wanted the ability to mark a feature as unstable during its development. Following the design of other tools in the Rust ecosystem like Cargo, I worked on the ability to have unstable feature flags and CLI options in #896, and I added some various consistency improvements in #897, #899, and #919.
I added the start of the witness feature in #893, and improved the contributor experience and documentation for adding new witnesses in #935 and #933.
Work on witnesses is currently unfinished ongoing, and this is one of the things I will keep working on as GSoC finishes.
lint groups
One of the stretch goals in my original project was adding configurable lint groups in addition to configuring individual lints, and this feature was seeming more useful than adding CLI configuration flags. I worked on a design draft, focusing on how a user would use these features. I started the work by reading the priority
field to the [lints]
table in #932 to avoid configuration conflicts by defining precedence, and I have drafts of the logic to have lint group configuration, so expect to see lint groups in cargo-semver-checks
pretty soon.
...and that's it. I merged 40 commits during GSoC which feels like a lot, but also not a lot, because each of those commits is squashed and many of them have several parts/squashed commits and (quite in-depth) discussions in the pull request, so the commit history does not tell the full story.
thanks
About those discussions: I could not get this far in the blog post without recognizing my mentor, Predrag Gruevski. His attention to detail (down to the number of spaces after a period), thoughtfulness, kindness, and careful consideration of every line of code I merged into cargo-semver-checks
(and the so many that I didn't) encouraged me to design, implement, and think about the code I submitted from different perspectives and as part of a complex system. This was so helpful in shaping the way I think about designing features and contributing to open source, and I can't thank him enough.
Additionally, the community around the Rust GSoC program and the Rust project in general was a super great environment to be a part of. Special thanks to the GSoC admin Jakub Beránek, maintainers and contributors like Ed Page and Scott Schafer, everyone else working on a Rust GSoC project this summer - it was such a great experience to see updates and how everyone's project progressed over the summer, and the whole open source community. I can't overstate what a welcoming experience this has been, and I'm definitely going to keep being a part of the community in the future.
and an apology
The first PR I opened as a part of my GSoC project was... a little overzealous. It changed hundreds of lines of code, affected multiple different systems, added multiple different features, and was overall a huge set of changes to review. My mentor (very nicely and diplomatically) suggested I might break up the changes into smaller PRs. This was a great learning experience for me to reflect on my git hygiene and how to make my changes easier and clearer to review, which is something I worked on and developed over the summer. I'm grateful to have had this opportunity to grow, and I do apologize for this first pull request ;).
what's next
This is certainly not the end of my work on cargo-semver-checks
. Right now, I have three projects I'm working on: adding the functionality for lint groups, keeping working on adding witnesses, and working on a refactor of the command line output of cargo-semver-checks
. With the new features, cargo-semver-checks
outgrew its old interface a little bit, and we're working on exploring making it more consistent with the output of rustc
and clippy
.
Additionally, I'm going to keep working on some of my own projects, and I'm also planning on contributing to some other Rust open-source projects now that GSoC is over. I'd like to keep posting about my journey on this blog, and I made a Mastodon for interacting with the open-source community. (and I'm always looking for cool people to follow!)
Thanks again to everyone who made this summer possible!
For more information about the project, I wrote a blog post describing the problem I set out to solve this summer.