Add input exclude, allow list in package (#40)

* Add exclude, allow lists in package

* Add test for input exclude

* Second dummy crate in test workspace

* New tests

* Fix cache key

* Forgot about flat()

* Reflect changes in docs

* New key in cache test

* Shorter job name

* Allowed to fail -> expected to fail

* Update README.md

Co-authored-by: Predrag Gruevski <2348618+obi1kenobi@users.noreply.github.com>

* Hash inputs in cache key

* Update cache test

* Update README

---------

Co-authored-by: Predrag Gruevski <2348618+obi1kenobi@users.noreply.github.com>
This commit is contained in:
Mieszko Grodzicki 2023-04-23 20:24:06 +02:00 committed by GitHub
parent 7eca687541
commit 8b981cde1a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 114 additions and 40 deletions

View file

@ -14,10 +14,12 @@ runs:
persist-credentials: true
path: ref_slice
- name: Create dummy crate
# This crate does not have a matching baseline on crates.io, so any try
# of checking it should make cargo-semver-checks fail.
run: cargo new cargo-semver-action-dummy --lib
# This crates do not have matching baselines on crates.io, so any try
# of checking them should make cargo-semver-checks fail.
run: |
cargo new cargo-semver-action-dummy --lib
cargo new cargo-semver-action-dummy-2 --lib
shell: bash
- name: Create workspace Cargo.toml
run: echo -e "[workspace]\nmembers=['ref_slice','cargo-semver-action-dummy']" > Cargo.toml
run: echo -e "[workspace]\nmembers=['ref_slice','cargo-semver-action-dummy','cargo-semver-action-dummy-2']" > Cargo.toml
shell: bash

View file

@ -36,7 +36,7 @@ jobs:
run: |
git fetch origin major_change
git checkout major_change
- name: Run the action (allowed to fail)
- name: Run the action (expected to fail)
id: action_major
uses: ./action/
with:

View file

@ -95,7 +95,7 @@ jobs:
run: |
RUSTC=$(rustc --version | sed -e 's/\s\+/-/g')
SEMVER_CHECKS=$(cargo semver-checks --version | sed -e 's/\s\+/-/g')
echo "KEY=testprefix-test-cache-exists-default-ref_slice-8e11dadaa21a8bf3112b6c764012883b-linux-$RUSTC-$SEMVER_CHECKS-da39a3ee5e6b4b0d3255bfef95601890afd80709-semver-checks-rustdoc" >> $GITHUB_OUTPUT
echo "KEY=testprefix-test-cache-exists-default-d45618ed191f0a73-linux-$RUSTC-$SEMVER_CHECKS-da39a3ee5e6b4b0d3255bfef95601890afd80709-semver-checks-rustdoc" >> $GITHUB_OUTPUT
- name: Download saved cache
uses: actions/cache/restore@v3
with:

View file

@ -26,7 +26,7 @@ jobs:
uses: ./action/
with:
package: ref_slice
- name: Run the action on the whole workspace (allowed to fail)
- name: Run the action on the whole workspace (expected to fail)
id: action_all
uses: ./action/
continue-on-error: true
@ -35,6 +35,17 @@ jobs:
run: |
echo "Error! The action should have failed because of checking the dummy crate, but it has not!"
exit 1
- name: Run the action on ref_slice fork and one dummy crate (expected to fail)
id: action_one_dummy
uses: ./action/
with:
package: ref_slice, cargo-semver-action-dummy
continue-on-error: true
- name: Fail if the action has not returned any errors (but it should have)
if: steps.action_one_dummy.outcome != 'failure'
run: |
echo "Error! The action should have failed because of checking the dummy crate, but it has not!"
exit 1
test-package-major:
name: Test input package (major change)
@ -48,7 +59,7 @@ jobs:
uses: ./action/.github/workflows/setup-test-workspace
with:
ref-slice-ref: major_change
- name: Run the action on ref_slice major change (allowed to fail)
- name: Run the action on ref_slice major change (expected to fail)
id: action_major
uses: ./action/
with:
@ -60,6 +71,56 @@ jobs:
echo "Error! The action should have failed because of the breaking change, but it has not."
exit 1
test-package-exclude-rio:
name: Test inputs package and exclude on Rio library
runs-on: ubuntu-latest
steps:
- name: Checkout the Rio repository
uses: actions/checkout@v3
with:
repository: oxigraph/rio
ref: 3bd01c2c977a0b01c918f6840cd05356477db358 # branch main
- name: Checkout the action
uses: actions/checkout@v3
with:
path: action
- name: Run the action using input package
uses: ./action/
with:
package: rio_api, rio_turtle, rio_xml
- name: Run the action using input exclude
uses: ./action/
with:
exclude: rio_testsuite
test-exclude:
name: Test input exclude
runs-on: ubuntu-latest
steps:
- name: Checkout the action
uses: actions/checkout@v3
with:
path: action
- name: Setup the workspace with ref_slice patch change
uses: ./action/.github/workflows/setup-test-workspace
with:
ref-slice-ref: patch_change
- name: Run the action excluding both dummy crates
uses: ./action/
with:
exclude: cargo-semver-action-dummy, cargo-semver-action-dummy-2
- name: Run the action excluding only one of the dummy crates (expected to fail)
id: action_major
uses: ./action/
with:
exclude: cargo-semver-action-dummy
continue-on-error: true
- name: Fail if the action has not returned any errors (but it should have)
if: steps.action_major.outcome != 'failure'
run: |
echo "Error! The action should have failed because of the breaking change, but it has not."
exit 1
test-verbose:
# There is currently no way of asserting that the output is indeed verbose,
# so we at least check if the action runs without an error when the
@ -101,7 +162,7 @@ jobs:
uses: ./action/
with:
manifest-path: ref_slice
- name: Run the action on the whole workspace (Cargo.toml path, allowed to fail)
- name: Run the action on the whole workspace (Cargo.toml path, expected to fail)
id: action_all_cargo_toml
uses: ./action/
with:
@ -125,7 +186,7 @@ jobs:
uses: ./action/.github/workflows/setup-test-workspace
with:
ref-slice-ref: major_change
- name: Run the action on ref_slice major change (Cargo.toml path, allowed to fail)
- name: Run the action on ref_slice major change (Cargo.toml path, expected to fail)
id: action_major_cargo_toml
uses: ./action/
with:
@ -136,7 +197,7 @@ jobs:
run: |
echo "Error! The action should have failed because of the breaking change, but it has not."
exit 1
- name: Run the action on ref_slice major change (crate path, allowed to fail)
- name: Run the action on ref_slice major change (crate path, expected to fail)
id: action_major_crate
uses: ./action/
with:
@ -174,7 +235,7 @@ jobs:
cd "ref slice"
git fetch origin major_change
git checkout major_change
- name: Run the action (allowed to fail)
- name: Run the action (expected to fail)
id: action_major
uses: ./action/
with:
@ -310,7 +371,7 @@ jobs:
exit 1
- name: Uninstall Rust
run: rustup self uninstall -y
- name: Run the action with manual rust-toolchain (allowed to fail)
- name: Run the action with manual rust-toolchain (expected to fail)
id: action_without_rust
uses: ./action/
with:

View file

@ -16,11 +16,12 @@ Every argument is optional.
| Input | Description | Default |
|--------------------|-----------------------------------------------------------------------------------------------------------------------------------|---------|
| `package` | The package whose API to check for semver (in Package Id Specification format, see https://doc.rust-lang.org/cargo/reference/pkgid-spec.html for reference). If not set, all packages defined in the Cargo.toml file are processed. | |
| `package` | Comma-separated list of the packages whose API to check for semver (in Package Id Specification format, see https://doc.rust-lang.org/cargo/reference/pkgid-spec.html for reference). If not set, all packages defined in the Cargo.toml file are processed. | |
| `exclude` | Comma-separated list of the packages that will be excluded from being processed. Has effect only if the input `package` is not specified. | |
| `manifest-path` | Path to Cargo.toml of crate or workspace to check. If not specified, the action assumes the manifest is under the default [`GITHUB_WORKSPACE`](https://docs.github.com/en/actions/learn-github-actions/variables#default-environment-variables) path. | |
| `verbose` | Enables verbose output of `cargo-semver-checks`. | `false` |
| `rust-toolchain` | Rust toolchain name to use, e.g. `stable`, `nightly` or `1.68.0`. It will be installed if necessary and used regardless of local overrides and the `rust-toolchain.toml` file. However, if the input is set to `manual`, the action assumes some Rust toolchain is already installed and uses the default one. | `stable` |
| `shared-key` | A cache key that will be used instead of the automatic key based on the name of the GitHub job and values of the inputs `package` and `manifest-path`. Might be provided e.g. to share the cache between the jobs. | |
| `shared-key` | A cache key that will be used instead of the automatic key based on the name of the GitHub job and values of the inputs `package`, `exclude` and `manifest-path`. Might be provided e.g. to share the cache between the jobs. | |
| `prefix-key` | Additional prefix of the cache key, can be set to start a new cache manually. | |
| `github-token` | The `GITHUB_TOKEN` secret used to download precompiled binaries from GitHub API. If not specified, the [automatic GitHub token](https://docs.github.com/en/actions/security-guides/automatic-token-authentication) provided to the workflow will be used. The token may be alternatively passed in an environment variable `GITHUB_TOKEN`. | `${{ github.token }}` |
@ -38,14 +39,23 @@ The action will work out-of-the-box if it is run inside the package root directo
# Use in workspaces with more than one crate
By default, if workspace contains multiple crates, all of them are checked for semver violations. You can specify a single crate to be checked instead using `package` or `manifest-path`.
By default, if workspace contains multiple crates, all of them are checked for semver violations. You can specify one or more crates to be checked instead using `package`, `exclude` or `manifest-path`.
For example, this will check `my-crate`:
For example, this will check `my-crate-api` and `my-crate-core`:
```yaml
- name: Check semver for my-crate from the current workspace
- name: Check semver for my-crate-api and my-crate-core
uses: obi1kenobi/cargo-semver-checks-action@v2
with:
package: my-crate
package: my-crate-api, my-crate-core
- name: Publish my-crate to crates.io
run: # your `cargo publish` code here
```
And this will process all crates from the current workspace except `my-crate-tests`:
```yaml
- name: Check semver for all crates except my-crate-tests
uses: obi1kenobi/cargo-semver-checks-action@v2
with:
exclude: my-crate-tests
- name: Publish my-crate to crates.io
run: # your `cargo publish` code here
```
@ -76,7 +86,7 @@ The two above might be also used together:
The action caches the baseline rustdoc for each package in the workspace. The keys used to distinguish the caches consist of four components:
- `prefix-key` input, which defaults to an empty string,
- `shared-key` input if provided, otherwise a concatenation of the value of environmental variable `GITHUB_JOB`, the value of `package` and the hashed value of the `manifest-path` variable (not the file it points to),
- `shared-key` input if provided, otherwise the value of environmental variable `GITHUB_JOB` concatenated with a hash of the values of inputs `package`, `exclude` and `manifest-path` (we hash the path itself, not the file it points to),
- internal, unchangable component, being a concatenation of the runner OS, `rustc` version, `cargo-semver-checks` version and hash of all `Cargo.lock` files in the current workspace,
- constant suffix `"semver-checks-rustdoc"`.

View file

@ -5,7 +5,10 @@ branding:
color: 'green'
inputs:
package:
description: 'The package whose API to check for semver (in Package Id Specification format, see https://doc.rust-lang.org/cargo/reference/pkgid-spec.html for reference). If not set, all packages defined in the Cargo.toml file are processed.'
description: 'Comma-separated list of the packages whose API to check for semver (in Package Id Specification format, see https://doc.rust-lang.org/cargo/reference/pkgid-spec.html for reference). If not set, all packages defined in the Cargo.toml file are processed.'
required: false
exclude:
description: 'Comma-separated list of the packages that will be excluded from being processed. Has effect only if the input `package` is not specified.'
required: false
manifest-path:
description: 'Path to Cargo.toml of crate or workspace to check. If not specified, the action assumes the manifest is under the default `GITHUB_WORKSPACE` path.'
@ -19,7 +22,7 @@ inputs:
description: 'Rust toolchain name to use, e.g. `stable`, `nightly` or `1.68.0`. It will be installed if necessary and used regardless of local overrides and the `rust-toolchain.toml` file. However, if the input is set to `manual`, the action assumes some Rust toolchain is already installed and uses the default one.'
default: 'stable'
shared-key:
description: 'A cache key that will be used instead of the automatic key based on the name of the GitHub job and values of the inputs `package` and `manifest-path`. Might be provided e.g. to share the cache between the jobs.'
description: 'A cache key that will be used instead of the automatic key based on the name of the GitHub job and values of the inputs `package`, `exclude` and `manifest-path`. Might be provided e.g. to share the cache between the jobs.'
required: false
default: ''
prefix-key:

2
dist/index.js vendored

File diff suppressed because one or more lines are too long

View file

@ -9,6 +9,7 @@ import {
getErrorMessage,
getPlatformMatchingTarget,
getRustcVersion,
optionFromList,
optionIfValueProvided,
} from "./utils";
import { RustdocCache } from "./rustdoc-cache";
@ -17,7 +18,8 @@ const CARGO_TARGET_DIR = path.join("semver-checks", "target");
function getCheckReleaseArguments(): string[] {
return [
optionIfValueProvided("--package", rustCore.input.getInput("package")),
optionFromList("--package", rustCore.input.getInputList("package")),
optionFromList("--exclude", rustCore.input.getInputList("exclude")),
optionIfValueProvided("--manifest-path", rustCore.input.getInput("manifest-path")),
rustCore.input.getInputBool("verbose") ? ["--verbose"] : [],
].flat();

View file

@ -1,16 +1,12 @@
import os = require("os");
import * as path from "path";
import * as crypto from "crypto";
import * as cache from "@actions/cache";
import * as core from "@actions/core";
import * as rustCore from "@actions-rs/core";
import {
getCargoSemverChecksVersion,
getRustcVersion,
hashFilesOrEmpty,
hashIfNotEmpty,
} from "./utils";
import { getCargoSemverChecksVersion, getRustcVersion, hashFilesOrEmpty } from "./utils";
export class RustdocCache {
private readonly cargo;
@ -71,11 +67,12 @@ export class RustdocCache {
}
private getRunDependentKey(): string {
return [
process.env["GITHUB_JOB"] || "",
rustCore.input.getInput("package"),
hashIfNotEmpty(rustCore.input.getInput("manifest-path")),
].join("-");
const hasher = crypto.createHash("md5");
hasher.update(JSON.stringify({ package: rustCore.input.getInputList("package").sort() }));
hasher.update(JSON.stringify({ exclude: rustCore.input.getInputList("exclude").sort() }));
hasher.update(JSON.stringify({ manifest_path: rustCore.input.getInput("manifest-path") }));
return [process.env["GITHUB_JOB"] || "", hasher.digest("hex").substring(0, 16)].join("-");
}
private getLocalCacheHash(): string {

View file

@ -3,7 +3,6 @@ import hashFiles = require("hash-files");
import * as exec from "@actions/exec";
import * as rustCore from "@actions-rs/core";
import { createHash } from "node:crypto";
export function getErrorMessage(error: unknown): string {
if (error instanceof Error) {
@ -31,6 +30,10 @@ export function optionIfValueProvided(option: string, value?: string): string[]
return value ? [option, value] : [];
}
export function optionFromList(option: string, values: string[]): string[] {
return values.map((value) => [option, value]).flat();
}
export function hashFilesOrEmpty(patterns: string[]): string {
try {
return hashFiles.sync({ files: patterns });
@ -39,10 +42,6 @@ export function hashFilesOrEmpty(patterns: string[]): string {
}
}
export function hashIfNotEmpty(str: string): string {
return createHash("md5").update(str).digest("hex");
}
function makeExecOptions(stdout: { s: string }): exec.ExecOptions {
return {
listeners: {