From 5b79ed4144a2e572fa25b3f204cc164a08cf1f20 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Sat, 20 Sep 2025 18:18:49 -0700 Subject: [PATCH 1/4] Add cargo-semver-checks This adds cargo-semver-checks to CI to help catch any unintended breaking changes to the API. --- .github/workflows/main.yml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index ac21ae7eda..d2b5269779 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -110,6 +110,20 @@ jobs: env: RUSTDOCFLAGS: -D warnings + check-version-bump: + name: Check version bump + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - run: rustup update stable && rustup default stable + - name: Install cargo-semver-checks + run: | + mkdir installed-bins + curl -Lf https://github.com/obi1kenobi/cargo-semver-checks/releases/download/v0.43.0/cargo-semver-checks-x86_64-unknown-linux-gnu.tar.gz \ + | tar -xz --directory=./installed-bins + echo `pwd`/installed-bins >> $GITHUB_PATH + - run: cargo semver-checks --workspace + # The success job is here to consolidate the total success/failure state of # all other jobs. This job is then included in the GitHub branch protection # rule which prevents merges unless all other jobs are passing. This makes @@ -125,6 +139,7 @@ jobs: - gui - clippy - docs + - check-version-bump runs-on: ubuntu-latest steps: - run: jq --exit-status 'all(.result == "success")' <<< '${{ toJson(needs) }}' From 2011ddb47940450b15cdb6626dbc6669021504f7 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 26 Sep 2025 17:48:43 +0000 Subject: [PATCH 2/4] Update cargo-semver-checks to v0.44.0 --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index d2b5269779..9e350f296b 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -119,7 +119,7 @@ jobs: - name: Install cargo-semver-checks run: | mkdir installed-bins - curl -Lf https://github.com/obi1kenobi/cargo-semver-checks/releases/download/v0.43.0/cargo-semver-checks-x86_64-unknown-linux-gnu.tar.gz \ + curl -Lf https://github.com/obi1kenobi/cargo-semver-checks/releases/download/v0.44.0/cargo-semver-checks-x86_64-unknown-linux-gnu.tar.gz \ | tar -xz --directory=./installed-bins echo `pwd`/installed-bins >> $GITHUB_PATH - run: cargo semver-checks --workspace From 4fc72e8d9f315f6990624661b9e9f1ad1dd38031 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Fri, 26 Sep 2025 17:37:46 -0700 Subject: [PATCH 3/4] Set up new workspace publish workflow This sets up the publish workflow to use the new OIDC authentication, and to publish the whole workspace at once. --- .github/workflows/deploy.yml | 10 ++++++++-- Cargo.toml | 14 +++++++------- .../mdbook-remove-emphasis/Cargo.toml | 1 + 3 files changed, 16 insertions(+), 9 deletions(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 82ba9ee7dc..859f191e3a 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -51,11 +51,17 @@ jobs: publish: name: Publish to crates.io runs-on: ubuntu-latest + permissions: + # Required for OIDC token exchange + id-token: write steps: - uses: actions/checkout@v4 - name: Install Rust (rustup) run: rustup update stable --no-self-update && rustup default stable + - name: Authenticate with crates.io + id: auth + uses: rust-lang/crates-io-auth-action@v1 - name: Publish env: - CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} - run: cargo publish --no-verify + CARGO_REGISTRY_TOKEN: ${{ steps.auth.outputs.token }} + run: cargo publish --workspace --no-verify diff --git a/Cargo.toml b/Cargo.toml index 4804376fd3..17ef449be4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -39,13 +39,13 @@ hex = "0.4.3" html5ever = "0.35.0" indexmap = "2.10.0" ignore = "0.4.23" -mdbook-core = { path = "crates/mdbook-core" } -mdbook-driver = { path = "crates/mdbook-driver" } -mdbook-html = { path = "crates/mdbook-html" } -mdbook-markdown = { path = "crates/mdbook-markdown" } -mdbook-preprocessor = { path = "crates/mdbook-preprocessor" } -mdbook-renderer = { path = "crates/mdbook-renderer" } -mdbook-summary = { path = "crates/mdbook-summary" } +mdbook-core = { path = "crates/mdbook-core", version = "0.5.0-alpha.1" } +mdbook-driver = { path = "crates/mdbook-driver", version = "0.5.0-alpha.1" } +mdbook-html = { path = "crates/mdbook-html", version = "0.5.0-alpha.1" } +mdbook-markdown = { path = "crates/mdbook-markdown", version = "0.5.0-alpha.1" } +mdbook-preprocessor = { path = "crates/mdbook-preprocessor", version = "0.5.0-alpha.1" } +mdbook-renderer = { path = "crates/mdbook-renderer", version = "0.5.0-alpha.1" } +mdbook-summary = { path = "crates/mdbook-summary", version = "0.5.0-alpha.1" } memchr = "2.7.5" notify = "8.1.0" notify-debouncer-mini = "0.6.0" diff --git a/examples/remove-emphasis/mdbook-remove-emphasis/Cargo.toml b/examples/remove-emphasis/mdbook-remove-emphasis/Cargo.toml index 8b2edd03a1..74ced3126a 100644 --- a/examples/remove-emphasis/mdbook-remove-emphasis/Cargo.toml +++ b/examples/remove-emphasis/mdbook-remove-emphasis/Cargo.toml @@ -2,6 +2,7 @@ name = "mdbook-remove-emphasis" version = "0.1.0" edition.workspace = true +publish = false [dependencies] mdbook-preprocessor.workspace = true From 2c7d192b5041e393cc9c3d7e4d8d3bffa58d9de4 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Fri, 26 Sep 2025 18:47:59 -0700 Subject: [PATCH 4/4] Add an xtask to help with running tests During development I often need to run a bunch of tests. Instead of having some unwieldy shell command, I have added this xtask to help with running the testing commands. --- .cargo/config.toml | 2 + CONTRIBUTING.md | 24 ++++++-- Cargo.lock | 4 ++ crates/xtask/Cargo.toml | 12 ++++ crates/xtask/README.md | 4 ++ crates/xtask/src/main.rs | 122 +++++++++++++++++++++++++++++++++++++++ 6 files changed, 162 insertions(+), 6 deletions(-) create mode 100644 .cargo/config.toml create mode 100644 crates/xtask/Cargo.toml create mode 100644 crates/xtask/README.md create mode 100644 crates/xtask/src/main.rs diff --git a/.cargo/config.toml b/.cargo/config.toml new file mode 100644 index 0000000000..a4ce18e9e1 --- /dev/null +++ b/.cargo/config.toml @@ -0,0 +1,2 @@ +[alias] +xtask = "run --manifest-path=crates/xtask/Cargo.toml --" diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 03083d66b6..571ea27011 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -134,6 +134,21 @@ The main test harness is described in the [testsuite documentation](tests/testsu - `cargo clippy --workspace --all-targets --no-deps -- -D warnings` — This makes sure that there are no clippy warnings. - `RUSTDOCFLAGS="-D warnings" cargo doc --workspace --document-private-items --no-deps` — This verifies that there aren't any rustdoc warnings. - `cargo fmt --check` — Verifies that everything is formatted correctly. +- `cargo +stable semver-checks` — Verifies that no SemVer breaking changes have been made. You must install [`cargo-semver-checks`](https://crates.io/crates/cargo-semver-checks) first. + +To help simplify running all these commands, you can run the following cargo command: + +```sh +cargo xtask test-all +``` + +It is useful to run all tests before submitting a PR. While developing I recommend to run some subset of that command based on what you are working on. There are individual arguments for each one. For example: + +```sh +cargo xtask test-workspace clippy doc eslint fmt gui semver-checks +``` + +While developing, remove any of those arguments that are not relevant to what you are changing, or are really slow. ## Making a pull-request @@ -208,12 +223,9 @@ Instructions for mdBook maintainers to publish a new release: 1. Create a PR to update the version and update the CHANGELOG: 1. Update the version in `Cargo.toml` - 2. Run `cargo test` to verify that everything is passing, and to update `Cargo.lock`. - 3. Double-check for any SemVer breaking changes. - Try [`cargo-semver-checks`](https://crates.io/crates/cargo-semver-checks), though beware that the current version of mdBook isn't properly adhering to SemVer due to the lack of `#[non_exhaustive]` and other issues. See https://github.com/rust-lang/mdBook/issues/1835. - 4. Update `CHANGELOG.md` with any changes that users may be interested in. - 5. Update `continuous-integration.md` to update the version number for the installation instructions. - 6. Commit the changes, and open a PR. + 2. Run `cargo xtask test-all` to verify that everything is passing, and to update `Cargo.lock`. + 3. Update `CHANGELOG.md` with any changes that users may be interested in. + 4. Commit the changes, and open a PR. 2. After the PR has been merged, create a release in GitHub. This can either be done in the GitHub web UI, or on the command-line: ```bash MDBOOK_VERS="`cargo read-manifest | jq -r .version`" ; \ diff --git a/Cargo.lock b/Cargo.lock index e913aa3040..963a21ed61 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2514,6 +2514,10 @@ dependencies = [ "markup5ever 0.11.0", ] +[[package]] +name = "xtask" +version = "0.0.0" + [[package]] name = "zerocopy" version = "0.8.26" diff --git a/crates/xtask/Cargo.toml b/crates/xtask/Cargo.toml new file mode 100644 index 0000000000..fa10586d12 --- /dev/null +++ b/crates/xtask/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "xtask" +publish = false +edition.workspace = true +license.workspace = true +repository.workspace = true +rust-version.workspace = true + +[dependencies] + +[lints] +workspace = true diff --git a/crates/xtask/README.md b/crates/xtask/README.md new file mode 100644 index 0000000000..338bedaf22 --- /dev/null +++ b/crates/xtask/README.md @@ -0,0 +1,4 @@ +# xtask + +This is a CLI utility for running development commands for mdbook. +See [CONTRIBUTING.md](../../CONTRIBUTING.md) for how to use this. diff --git a/crates/xtask/src/main.rs b/crates/xtask/src/main.rs new file mode 100644 index 0000000000..6bd891cdb8 --- /dev/null +++ b/crates/xtask/src/main.rs @@ -0,0 +1,122 @@ +//! Helper for local development. + +use std::collections::BTreeMap; +use std::error::Error; +use std::process::Command; +use std::process::exit; + +type Result = std::result::Result>; + +fn main() -> Result<()> { + macro_rules! commands { + ($($name:literal => $func:ident),* $(,)?) => { + [$(($name, $func as fn() -> Result<()>)),*] + }; + } + + let cmds: BTreeMap<&'static str, fn() -> Result<()>> = commands! { + "test-all" => test_all, + "test-workspace" => test_workspace, + "clippy" => clippy, + "doc" => doc, + "fmt" => fmt, + "semver-checks" => semver_checks, + "eslint" => eslint, + "gui" => gui, + } + .into_iter() + .collect(); + let keys = cmds.keys().copied().collect::>().join(", "); + let mut args = std::env::args().skip(1).peekable(); + if args.peek().is_none() { + eprintln!("error: specify a command (valid options: {keys})"); + exit(1); + } + for arg in args { + if let Some(cmd_fn) = cmds.get(arg.as_str()) { + cmd_fn()?; + } else if matches!(arg.as_str(), "-h" | "--help") { + println!("valid options: {keys}"); + exit(0) + } else { + eprintln!("error: unknown command `{arg}` (valid options: {keys}"); + exit(1); + } + } + println!("all tests passed!"); + Ok(()) +} + +fn test_all() -> Result<()> { + test_workspace()?; + clippy()?; + doc()?; + fmt()?; + semver_checks()?; + eslint()?; + gui()?; + Ok(()) +} + +fn cargo(args: &str, cb: &dyn Fn(&mut Command)) -> Result<()> { + println!("Running `cargo {args}`"); + let mut cmd = Command::new("cargo"); + cmd.args(args.split_whitespace()); + cb(&mut cmd); + let status = cmd.status().expect("cargo should be installed"); + if !status.success() { + return Err("command `cargo {args}` failed".into()); + } + Ok(()) +} + +fn test_workspace() -> Result<()> { + cargo("test --workspace", &|_| {})?; + cargo("test --workspace --no-default-features", &|_| {})?; + Ok(()) +} + +fn clippy() -> Result<()> { + cargo( + "clippy --workspace --all-targets --no-deps -- -D warnings", + &|_| {}, + )?; + Ok(()) +} + +fn doc() -> Result<()> { + cargo( + "doc --workspace --document-private-items --no-deps", + &|cmd| { + cmd.env("RUSTDOCFLAGS", "-D warnings"); + }, + )?; + Ok(()) +} + +fn fmt() -> Result<()> { + cargo("fmt --check", &|_| {})?; + Ok(()) +} + +fn semver_checks() -> Result<()> { + cargo("+stable semver-checks --workspace", &|_| {})?; + Ok(()) +} + +fn gui() -> Result<()> { + cargo("test --test gui", &|_| {})?; + Ok(()) +} + +fn eslint() -> Result<()> { + println!("Running `npm run lint`"); + let status = Command::new("npm") + .args(["run", "lint"]) + .status() + .expect("npm should be installed"); + if !status.success() { + return Err("eslint failed".into()); + } + Ok(()) +}