mirror of
https://github.com/obi1kenobi/cargo-semver-checks-action.git
synced 2024-11-25 17:39:33 +01:00
Add initial cargo-semver-checks action implementation.
This commit is contained in:
parent
192d7c246b
commit
ebc272edf7
3 changed files with 140 additions and 0 deletions
57
action.yml
Normal file
57
action.yml
Normal file
|
@ -0,0 +1,57 @@
|
|||
name: 'cargo-semver-checks'
|
||||
description: 'Ensure your Rust crate's public API follows semantic versioning'
|
||||
inputs:
|
||||
crate-name:
|
||||
description: 'The crate whose API to check for semver'
|
||||
required: false
|
||||
default: ''
|
||||
version-tag-prefix:
|
||||
description: 'The prefix to use for the git tag for a version; the default "v" creates tags like "v1.0.0"'
|
||||
required: false
|
||||
default: 'v'
|
||||
runs:
|
||||
using: "composite"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Install rust
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: nightly
|
||||
profile: minimal
|
||||
- run: |
|
||||
# Colorize output, since GitHub Actions terminals support color.
|
||||
export CARGO_TERM_COLOR=always
|
||||
|
||||
# Record the current git sha, so we can come back to it after generating the baseline.
|
||||
export CURRENT_GIT_SHA="$(git rev-parse HEAD)"
|
||||
|
||||
# Ensure this action's scripts are available to run on the path.
|
||||
echo "${{ github.action_path }}" >> $GITHUB_PATH
|
||||
|
||||
export PACKAGE_NAME="${{ inputs.crate-name }}"
|
||||
if [[ "$PACKAGE_NAME" == '' ]]; then
|
||||
export PACKAGE_NAME="$(find_workspace_crates.sh)"
|
||||
fi
|
||||
export PACKAGE_NAME_WITH_UNDERSCORES="$(echo $"PACKAGE_NAME" | tr '-' '_')"
|
||||
|
||||
# Switch to the tag for the correct baseline version,
|
||||
# then build rustdoc JSON.
|
||||
#
|
||||
# We *do not* want to record and reuse the target directory path
|
||||
# across different git commits, since it may be at a different location
|
||||
# in different commits.
|
||||
git checkout "${{ inputs.version-tag-prefix }}$(find_comparison_version.sh "$PACKAGE_NAME")"
|
||||
cargo +nightly rustdoc -- -Zunstable-options --output-format json
|
||||
mv "$(cargo metadata --format-version 1 | jq -r .target_directory)/doc/$PACKAGE_NAME_WITH_UNDERSCORES.json" /tmp/baseline.json
|
||||
|
||||
# Return to the original git sha.
|
||||
git checkout "$CURRENT_GIT_SHA"
|
||||
|
||||
# Build rustdoc JSON for the current version, and move it to /tmp/
|
||||
# so it doesn't get overwritten by the baseline build.
|
||||
cargo +nightly rustdoc -- -Zunstable-options --output-format json
|
||||
mv "$(cargo metadata --format-version 1 | jq -r .target_directory)/doc/$PACKAGE_NAME_WITH_UNDERSCORES.json" /tmp/current.json
|
||||
|
||||
# Check for semver violations.
|
||||
cargo install cargo-semver-checks
|
||||
cargo semver-checks check-release --current /tmp/current.json --baseline /tmp/baseline.json
|
60
find_comparison_version.sh
Normal file
60
find_comparison_version.sh
Normal file
|
@ -0,0 +1,60 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
# Script requirements:
|
||||
# - curl
|
||||
# - jq
|
||||
# - sort with `-V` flag, available in `coreutils-7`
|
||||
# On macOS this may require `brew install coreutils`.
|
||||
|
||||
# Fail on first error, on undefined variables, and on failures in pipelines.
|
||||
set -euo pipefail
|
||||
|
||||
# Go to the repo root directory.
|
||||
cd "$(git rev-parse --show-toplevel)"
|
||||
|
||||
# The first argument should be the name of a crate.
|
||||
CRATE_NAME="$1"
|
||||
|
||||
CURRENT_VERSION="$( \
|
||||
cargo metadata --format-version 1 | \
|
||||
jq --arg crate_name "$CRATE_NAME" --exit-status -r \
|
||||
'.packages[] | select(.name == $crate_name) | .version' \
|
||||
)" || (echo >&2 "No crate named $CRATE_NAME found in workspace."; exit 1)
|
||||
echo >&2 "Crate $CRATE_NAME current version: $CURRENT_VERSION"
|
||||
|
||||
# The leading whitespace is important! With it, we know that every version is both
|
||||
# preceded by and followed by whitespace. We use this fact to avoid matching
|
||||
# on substrings of versions.
|
||||
EXISTING_VERSIONS="
|
||||
$( \
|
||||
curl 2>/dev/null "https://crates.io/api/v1/crates/$CRATE_NAME" | \
|
||||
jq --exit-status -r .versions[].num \
|
||||
)"
|
||||
echo >&2 -e "Versions on crates.io:$EXISTING_VERSIONS\n"
|
||||
|
||||
# Use version sort (sort -V) to get all versions in ascending order, then use grep to:
|
||||
# - grab the first line that matches the current version (--max-count=1)
|
||||
# - only match full lines (--line-regexp)
|
||||
# - get one line of leading context (-B 1) i.e. the immediately-smaller version, if one exists
|
||||
# - explicitly opt out of trailing context lines (-A 0)
|
||||
# Finally, use `head` to output only the first of the up-to-two lines output.
|
||||
# Now, either:
|
||||
# - two lines were output, and we grabbed the immediately-smaller version, or
|
||||
# - one line was output with only our version, because there was no immediately-smaller version,
|
||||
# and we grabbed that one. We sort this out with the subsequent conditional.
|
||||
OUTPUT="$( \
|
||||
echo -e "$CURRENT_VERSION$EXISTING_VERSIONS" | \
|
||||
sort -V | \
|
||||
grep -B 1 -A 0 --line-regexp --max-count=1 "$CURRENT_VERSION" | \
|
||||
head -n 1 \
|
||||
)"
|
||||
|
||||
if [[ "$OUTPUT" == "$CURRENT_VERSION" ]]; then
|
||||
echo >&2 "There is no suitable comparison version."
|
||||
echo >&2 \
|
||||
"The current version $CURRENT_VERSION is smaller than any version published on crates.io"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Comparison version: $OUTPUT" >&2
|
||||
echo "$OUTPUT"
|
23
find_workspace_crates.sh
Normal file
23
find_workspace_crates.sh
Normal file
|
@ -0,0 +1,23 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
# Script requirements:
|
||||
# - jq
|
||||
|
||||
# Fail on first error, on undefined variables, and on failures in pipelines.
|
||||
set -euo pipefail
|
||||
|
||||
# Go to the repo root directory.
|
||||
cd "$(git rev-parse --show-toplevel)"
|
||||
|
||||
crates="$(cargo metadata --format-version 1 | \
|
||||
jq --exit-status -r \
|
||||
'.workspace_members[] as $key | .packages[] | select(.id == $key) | .name')"
|
||||
crate_count="$(echo -e "${crates}" | wc -l)"
|
||||
|
||||
if [[ "$crate_count" == "1" ]]; then
|
||||
echo -e "${crates}"
|
||||
exit 0
|
||||
else
|
||||
echo >&2 "Multiple crates in workspace, please specify a crate in the 'crate-name' setting."
|
||||
exit 1
|
||||
fi
|
Loading…
Reference in a new issue