elharo commented on issue #91:
URL:
https://github.com/apache/maven-artifact-plugin/issues/91#issuecomment-5846872300
## Proposed feature: verifying artifact checksums
This is a draft of the user-facing description for the new goal, to be
turned into documentation once the goal exists. Feedback on the design choices
(in particular the two questions at the end) is welcome.
### The problem
Every ASF release publishes checksum files next to the artifacts: `.sha1`
and `.md5` for everything in the repository, and additionally `.sha512` for the
source release archive.
To confirm that the file on your disk is the file that was actually
published, you have to compute a digest of your local copy and compare it with
the published value. Doing that by eye is unpleasant and error-prone:
- a single transposed character in a 128 character hex string is very hard
to spot,
- it is easy to skip an artifact, because there is no list telling you what
should have been checked,
- the repetition is tedious, since a release of a multi-module project has
one artifact per module plus sources, javadoc, the BOM and the source release
archive.
The plugin already knows how to compute digests of build output:
`artifact:buildinfo` records a `sha512` for every file it describes, and
`artifact:describe-build-output` prints a `sha256` next to each file. What is
missing is the comparison against a value that was published somewhere else,
which is exactly the step a release manager performs by hand today.
### The goal
A goal that computes the digest of the artifacts your build has produced,
compares it with the expected value that you supply, and fails the build if
they differ.
It is a verification tool: it does not publish or generate anything. You
tell it what the published checksum is supposed to be, and it tells you whether
your local file agrees with it.
### Usage
#### Verifying the main artifact
The simplest case is the one in the original report: the main artifact of
the project, that is the jar for `jar` packaging and the POM for `pom`
packaging.
```
$ mvn clean verify artifact:verify-checksum -Dsha512=1a2b3c4d5e6f7890...
```
If the computed digest equals the supplied value, the goal reports a match
and the build continues. If it differs, the build fails and both values are
printed so that the difference can be seen.
#### Verifying a particular attached artifact
Most release artifacts are attached artifacts, identified by a classifier
and an extension, and are named that way in the published file name. Use the
same pair in brackets after the algorithm name:
```
$ mvn clean verify artifact:verify-checksum
"-Dsha512[source-release:zip]=4d5e6f708192a3b4..."
```
- the part before the brackets is the digest algorithm,
- the part in brackets is `<classifier>:<extension>`,
- the main artifact, which has no classifier, is the short form with no
brackets at all.
To verify the POM of a `jar` project, which has no classifier either, leave
the classifier empty:
```
$ mvn clean verify artifact:verify-checksum "-Dsha512[:pom]=7c8d9e0f1a2b..."
```
> **Quote the property.** The square brackets are glob characters in most
shells. Unquoted, the argument may be expanded away or, in shells that fail on
unmatched globs, abort the command. Always write
`-D"sha512[source-release:zip]=..."`.
#### Verifying several artifacts at once
Repeat the property, once per artifact, in a single invocation. This is the
shape of a full release verification, for `maven-artifact-plugin` 3.7.0:
```
$ mvn clean deploy artifact:verify-checksum \
-Dsha512=1a2b3c4d5e6f7890... \
"-Dsha512[:pom]=7c8d9e0f1a2b..." \
"-Dsha512[sources:jar]=9f0a1b2c3d4e..." \
"-Dsha512[javadoc:jar]=5a6b7c8d9e0f..." \
"-Dsha512[source-release:zip]=4d5e6f708192..." \
"-Dsha512[cyclonedx:xml]=1b2c3d4e5f60..." \
"-Dsha512[cyclonedx:json]=7e8f9a0b1c2d..."
```
Every artifact named must be present in the build and every digest must
match, otherwise the build fails.
#### Publishing a repeatable configuration
Long command lines are easy to mistype and awkward to keep. The expected
values can be declared in the POM instead, which also makes them reviewable and
repeatable, and lets the check run as part of the build:
```xml
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-artifact-plugin</artifactId>
<version>3.7.1-SNAPSHOT</version>
<configuration>
<checksums>
<checksum>
<algorithm>sha512</algorithm>
<expected>1a2b3c4d5e6f7890...</expected>
</checksum>
<checksum>
<algorithm>sha512</algorithm>
<classifier>source-release</classifier>
<extension>zip</extension>
<expected>4d5e6f708192a3b4...</expected>
</checksum>
<checksum>
<algorithm>sha1</algorithm>
<expected>2b3c4d5e6f708192...</expected>
</checksum>
</checksums>
</configuration>
</plugin>
</plugins>
</build>
```
An entry without `classifier` and `extension` refers to the main artifact.
An entry without `algorithm` defaults to `sha512`, which is the algorithm the
ASF dist area publishes.
Values given on the command line are combined with the ones in the POM, so a
CI job can verify the artifacts the POM covers and add a few more for a
specific run.
#### Running the check as part of the build
The goal is not bound to a lifecycle phase by default and is meant to be
invoked from the command line, like `artifact:compare` and
`artifact:describe-build-output`. It can be bound to a phase once the artifacts
it verifies exist, typically `verify` or `deploy`:
```xml
<executions>
<execution>
<id>verify-checksums</id>
<phase>verify</phase>
<goals>
<goal>verify-checksum</goal>
</goals>
</execution>
</executions>
```
#### Multi-module builds
In a multi-module build each module has its own main artifact, and Maven
scopes properties per project, so the goal can be run for one module at a time:
```
$ mvn clean install
$ mvn -pl core artifact:verify-checksum -Dsha512=1a2b3c4d5e6f7890...
$ mvn -pl compat artifact:verify-checksum -Dsha512=3d4e5f607182...
```
Alternatively, declare the `<checksums>` configuration in each module's own
POM, which keeps the verification next to the module it belongs to and lets one
command verify the whole reactor.
### Behaviour
**Algorithms.** `sha1`, `sha256`, `sha512` and `md5` are supported, and the
name is matched regardless of case. `sha1` and `md5` are weak digests, but they
are published by the ASF for every artifact, so verifying them is still useful:
a match confirms the file is the published one, and that is what the release
manager is trying to establish.
**Values.** The supplied value is compared after trimming surrounding
whitespace, so it does not matter whether the value is copied from a `.sha512`
file with its trailing newline or typed by hand. Comparison is
case-insensitive, since hex digits are sometimes pasted in uppercase.
**Mismatches.** A mismatch fails the build and reports the algorithm, the
artifact, the expected value and the value that was actually computed, so that
a wrong value can be spotted and re-copied without re-running anything.
**Missing artifacts.** If a checksum is supplied for an artifact that the
build did not produce, for example because the release profile was not active,
the build fails. A verification that silently checks nothing is worse than no
verification.
**Uncovered artifacts.** Artifacts that no expected value was supplied for
are not verified and are not an error, so the single property case above stays
usable on a project that also produces sources and javadoc jars. A summary line
reports how many artifacts were verified, how many mismatched and how many were
not covered, so an incomplete run is visible in the log.
**Nothing to verify.** If no expected checksum is supplied at all, either on
the command line or in the POM, the goal reports this and does not fail, so
that it can be safely left in a POM while the expected values are still being
filled in.
### Example output
```
[INFO] --- maven-artifact-plugin:3.7.1-SNAPSHOT:verify-checksum
(default-cli) @ maven-artifact-plugin ---
[INFO] Verifying 7 checksums of maven-artifact-plugin:3.7.0
[INFO] sha512 matches maven-artifact-plugin-3.7.0.jar
[INFO] sha512 matches maven-artifact-plugin-3.7.0.pom
[INFO] sha512 matches maven-artifact-plugin-3.7.0-sources.jar
[INFO] sha512 matches maven-artifact-plugin-3.7.0-javadoc.jar
[INFO] sha512 matches maven-artifact-plugin-3.7.0-source-release.zip
[INFO] sha1 matches maven-artifact-plugin-3.7.0.jar
[INFO] md5 matches maven-artifact-plugin-3.7.0.jar
[INFO] Checksum verification result: 7 verified, 0 mismatched, 0 missing
```
and on a mismatch:
```
[ERROR] sha512 mismatch for maven-artifact-plugin-3.7.0-source-release.zip
[ERROR] expected
4d5e6f708192a3b4c5d6e7f8091a2b3c4d5e6f708192a3b4c5d6e7f8091a2b3c
[ERROR] actual
4d5e6f708192a3b4c5d6e7f8091a2b3c4d5e6f708192a3b4c5d6e7f8091a2b3d
[ERROR] Checksum verification result: 6 verified, 1 mismatched, 0 missing
[ERROR] Failed to execute goal
org.apache.maven.plugins:maven-artifact-plugin:3.7.1-SNAPSHOT:verify-checksum
(default-cli) on project maven-artifact-plugin: checksum verification failed
```
### Open questions
Two design choices are worth settling before this is implemented.
**1. How to identify the artifact on the command line.** This draft uses
`<algorithm>[<classifier>:<extension>]`, so `sha512[source-release:zip]`, which
is the order that matches the published file name
`maven-artifact-plugin-3.7.0-source-release.zip` and is what the original
report proposed. The alternatives are the opposite order,
`sha512[zip:source-release]`, which is the order Maven itself uses in
coordinates like `groupId:artifactId:version:type:classifier`, or the published
file name, `sha512[maven-artifact-plugin-3.7.0-source-release.zip]`, which is
unambiguous but verbose, especially for a multi-module project where it has to
be repeated per module. Any of the three is implementable; the first was chosen
because it is the shortest of the unambiguous options and matches the file the
user has just downloaded.
**2. Whether the expected values should also be readable from a file.** The
release manager normally downloads the published `.sha512` file rather than
copying its content out of a web page. Accepting a path to that file, or to a
directory of checksum files, would remove the last manual step and would also
make a multi-module release practical in one invocation, because one manifest
can cover every module. That is a larger addition than the goal asked for here,
so it is raised here rather than assumed.
### Related
- Original report:
[MARTIFACT-1](https://issues.apache.org/jira/browse/MARTIFACT-1)
- Goals that already expose computed digests: `artifact:buildinfo` (records
`sha512`) and `artifact:describe-build-output` (prints `sha256`)
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]