In our efforts to provide developers with the best tools for packaging software, we have always prioritized the user experience. One of the most effective ways to ensure a seamless build process is automatic linting.
In this post, we’ll look at how we evolved linting from an app-specific feature into a centralized service for our growing family of craft apps, including Snapcraft, Charmcraft, and Rockcraft.
Craft linters will have opinions on your artifact
Linting is immediate feedback on the quality and correctness of a software artifact. In the context of the craft apps, linters can help prevent functional issues in your artifact before they’re published. They also check whether the artifact complies with safety policies. With both types of correctness, the linter gives you practical advice for how to resolve the issue. For example, the linters in Snapcraft detect unused system libraries that unnecessarily increase the size of the snap.
I think it’s very important that our linters recommend solutions. This design has been gaining momentum among linters throughout the industry. We draw inspiration from popular open-source linters, like Clippy for Rust and Ruff for Python. Developers value inline advice from their linters, and these two were designed to meet that need.
The history of craft linters
Initially, the craft apps independently developed their own linting capabilities. In 2021, Charmcraft 1.2 was the first to support linting. It validates the contents of charms and verifies the charm’s entrypoint. The linter runs when the charm is packed and when you run the analyze command for existing charms.
A year later, Snapcraft 7 introduced dedicated linters for verifying binary file parameters for classically-confined snaps and for detecting missing and unused system libraries. It also added a standalone lint command for existing snaps.
Snapcraft’s linting framework was designed to make it easy to add new linters. The benefits of this design became apparent in Snapcraft 8, when a community contributor, Soumyadeep Ghosh, added a new metadata linter. It checks for essential details like links to source code and issue trackers
Snapcraft 9 added a fourth linter for snaps that use GPU libraries. It recommends the use of the recent GPU extension and its accompanying content snap to reduce library duplication.
Because Snapcraft and Charmcraft developed their linting logic separately, they shared no code. The user experience differed between them, with Charmcraft using analyze while Snapcraft uses lint. To fulfill our goal of a consistent user experience, we needed a common implementation.
A unified linter service
In 2023, a year after Snapcraft introduced its linters, we created a library called Craft Application. This library implements mechanisms common to all craft apps. With this library, we now had a place to unify our efforts.
This year, we introduced a dedicated Linter Service to the Craft Application library. This service, which emerged as a community contribution from Hamdaan Ali Quatil, provides a common framework for all craft apps to implement linters.
Centralizing this functionality ensures that lint messages remains predictable and consistent across the craft apps. The following examples show the standardized format generated by the Linter Service for both project file validation and artifact validation:
$ testcraft lint
lint results:
testcraft.missing_version:
- [WARNING] TC001: project is missing the recommended 'version' field (testcraft.yaml)
Possible issues found in testcraft.yaml
$ testcraft lint --post my-artifact-1.0-amd64.testcraft
lint results:
testcraft.empty_artifact:
- [ERROR] TC100: artifact is empty other than metadata.yaml (metadata.yaml)
Errors found in artifacts
Next steps for us
The introduction of the Linter Service is just the beginning. We’re exploring several possibilities to extend linting.
The first is pre-linters. These would validate project files before the build lifecycle even begins, catching errors as early as possible. As a maintainer, you shouldn’t need to wait for an entire build to complete before seeing problems that can be spotted upfront.
Next would be linters in Debcraft. Our new app for building Debian packages will leverage this service to provide a consistent interface, combining traditional checks from Lintian with Debcraft-specific validation.
The last we’re considering is Snap Store readiness. All snaps uploaded to the Snap Store go through a series of readiness checks conducted by the snappy-review tool. Snapcraft can run this tooling locally in a dedicated linter.
The shift to a centralized Linter Service is more than an architectural cleanup. By moving linting into a common service, these improvements can benefit every craft app, and you can expect a consistent experience as you move between them. As our ecosystem grows, this helps ensure that quality remains an inherent property of every artifact you build, not an afterthought.
I’m excited to see how this unified service will streamline how you make artifacts.