diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 9ac97b413b..f086e8586e 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -12,5 +12,5 @@ jobs: test: uses: FuelLabs/github-actions/.github/workflows/mdbook-docs.yml@master with: - docs-src-path: 'docs/src' - + docs-src-path: "docs/src" + pre-command: 'cargo run --package versions-replacer -- ./docs --manifest-path ./Cargo.toml --filename-regex "\.md$"' diff --git a/Cargo.toml b/Cargo.toml index 0a6a2dbcae..7d580165b0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,6 +25,7 @@ members = [ "packages/fuels-test-helpers", "packages/wasm-tests", "scripts/check-docs", + "scripts/versions-replacer", ] [workspace.package] diff --git a/docs/src/calling-contracts/call-params.md b/docs/src/calling-contracts/call-params.md index 75d0198977..27506ca255 100644 --- a/docs/src/calling-contracts/call-params.md +++ b/docs/src/calling-contracts/call-params.md @@ -9,7 +9,7 @@ The parameters for a contract call are: 3. Gas forwarded -You can use these to forward coins to a contract. You can configure these parameters by creating an instance of [`CallParameters`](https://docs.rs/fuels/latest/fuels/programs/contract/struct.CallParameters.html) and passing it to a chain method called `call_params`. +You can use these to forward coins to a contract. You can configure these parameters by creating an instance of [`CallParameters`](https://docs.rs/fuels/{{versions.fuels}}/fuels/programs/contract/struct.CallParameters.html) and passing it to a chain method called `call_params`. For instance, suppose the following contract that uses Sway's `msg_amount()` to return the amount sent in that transaction. diff --git a/docs/src/connecting/querying.md b/docs/src/connecting/querying.md index 7f3dfbe8bc..244d20b693 100644 --- a/docs/src/connecting/querying.md +++ b/docs/src/connecting/querying.md @@ -1,6 +1,6 @@ # Querying the blockchain -Once you set up a provider, you can interact with the Fuel blockchain. Here are a few examples of what you can do with a provider; for a more in-depth overview of the API, check the [official provider API documentation](https://docs.rs/fuels/latest/fuels/accounts/provider/struct.Provider.html). +Once you set up a provider, you can interact with the Fuel blockchain. Here are a few examples of what you can do with a provider; for a more in-depth overview of the API, check the [official provider API documentation](https://docs.rs/fuels/{{versions.fuels}}/fuels/accounts/provider/struct.Provider.html). - [Set up](#set-up) - [Get all coins from an address](#get-all-coins-from-an-address) diff --git a/docs/src/getting-started.md b/docs/src/getting-started.md index 11223424ea..d31ca6ccf1 100644 --- a/docs/src/getting-started.md +++ b/docs/src/getting-started.md @@ -91,10 +91,10 @@ cargo test -- --nocapture Add these dependencies on your `Cargo.toml`: ```toml -fuels = "0.48" +fuels = "{{versions.fuels}}" ``` -> **Note** We're using version `0.48` of the SDK, which is the latest version at the time of this writing. +> **Note** We're using version `{{versions.fuels}}` of the SDK, which is the latest version at the time of this writing. And then, in your Rust file that's going to make use of the SDK: diff --git a/docs/src/reference.md b/docs/src/reference.md index f031a27969..f9f76c7911 100644 --- a/docs/src/reference.md +++ b/docs/src/reference.md @@ -1,3 +1,3 @@ # API Reference -For a more in-depth look at the APIs provided by the Fuel Rust SDK, head over to the [official documentation](https://docs.rs/fuels/latest/fuels/). In the actual Rust docs, you can see the most up-to-date information about the API, which is synced with the code as it changes. +For a more in-depth look at the APIs provided by the Fuel Rust SDK, head over to the [official documentation](https://docs.rs/fuels/{{versions.fuels}}/fuels/). In the actual Rust docs, you can see the most up-to-date information about the API, which is synced with the code as it changes. diff --git a/docs/src/types/address.md b/docs/src/types/address.md index 2480a72918..3aac7381f5 100644 --- a/docs/src/types/address.md +++ b/docs/src/types/address.md @@ -1,6 +1,6 @@ # Address -Like `Bytes32`, `Address` is a wrapper on `[u8; 32]` with similar methods and implements the same traits (see [fuel-types documentation](https://docs.rs/fuel-types/latest/fuel_types/struct.Address.html)). +Like `Bytes32`, `Address` is a wrapper on `[u8; 32]` with similar methods and implements the same traits (see [fuel-types documentation](https://docs.rs/fuel-types/{{versions.fuel-types}}/fuel_types/struct.Address.html)). These are the main ways of creating an `Address`: diff --git a/docs/src/types/asset-id.md b/docs/src/types/asset-id.md index ff7affce22..71f43348aa 100644 --- a/docs/src/types/asset-id.md +++ b/docs/src/types/asset-id.md @@ -1,6 +1,6 @@ # AssetId -Like `Bytes32`, `AssetId` is a wrapper on `[u8; 32]` with similar methods and implements the same traits (see [fuel-types documentation](https://docs.rs/fuel-types/latest/fuel_types/struct.AssetId.html)). +Like `Bytes32`, `AssetId` is a wrapper on `[u8; 32]` with similar methods and implements the same traits (see [fuel-types documentation](https://docs.rs/fuel-types/{{versions.fuel-types}}/fuel_types/struct.AssetId.html)). These are the main ways of creating an `AssetId`: diff --git a/docs/src/types/bytes32.md b/docs/src/types/bytes32.md index 87fc801db1..041fc2d46e 100644 --- a/docs/src/types/bytes32.md +++ b/docs/src/types/bytes32.md @@ -14,6 +14,6 @@ These are the main ways of creating a `Bytes32`: {{#include ../../../examples/types/src/lib.rs:bytes32_format}} ``` -For a full list of implemented methods and traits, see the [fuel-types documentation](https://docs.rs/fuel-types/latest/fuel_types/struct.Bytes32.html). +For a full list of implemented methods and traits, see the [fuel-types documentation](https://docs.rs/fuel-types/{{versions.fuel-types}}/fuel_types/struct.Bytes32.html). > **Note:** In Fuel, there's a special type called `b256`, which is similar to `Bytes32`; also used to represent hashes, and it holds a 256-bit value. In Rust, through the SDK, this is represented as `Bits256(value)` where `value` is a `[u8; 32]`. If your contract method takes a `b256` as input, all you need to do is pass a `Bits256([u8; 32])` when calling it from the SDK. diff --git a/docs/src/types/contract-id.md b/docs/src/types/contract-id.md index 6a16e38e38..a14dd877ce 100644 --- a/docs/src/types/contract-id.md +++ b/docs/src/types/contract-id.md @@ -1,6 +1,6 @@ # ContractId -Like `Bytes32`, `ContractId` is a wrapper on `[u8; 32]` with similar methods and implements the same traits (see [fuel-types documentation](https://docs.rs/fuel-types/latest/fuel_types/struct.ContractId.html)). +Like `Bytes32`, `ContractId` is a wrapper on `[u8; 32]` with similar methods and implements the same traits (see [fuel-types documentation](https://docs.rs/fuel-types/{{versions.fuel-types}}/fuel_types/struct.ContractId.html)). These are the main ways of creating a `ContractId`: diff --git a/scripts/versions-replacer/Cargo.toml b/scripts/versions-replacer/Cargo.toml new file mode 100644 index 0000000000..8dda125775 --- /dev/null +++ b/scripts/versions-replacer/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "versions-replacer" +publish = false +authors.workspace = true +edition.workspace = true +homepage.workspace = true +readme.workspace = true +license.workspace = true +repository.workspace = true +rust-version.workspace = true +version.workspace = true + +[dependencies] +argh = "0.1.12" +cargo_metadata = "0.17.0" +color-eyre = "0.6.2" +once_cell = "1.18.0" +regex = { workspace = true } +serde = { workspace = true, features = ["derive"] } +walkdir = "2.3.3" diff --git a/scripts/versions-replacer/src/lib.rs b/scripts/versions-replacer/src/lib.rs new file mode 100644 index 0000000000..b019f47b1c --- /dev/null +++ b/scripts/versions-replacer/src/lib.rs @@ -0,0 +1,2 @@ +pub mod metadata; +pub mod replace; diff --git a/scripts/versions-replacer/src/main.rs b/scripts/versions-replacer/src/main.rs new file mode 100644 index 0000000000..a5d420e1b1 --- /dev/null +++ b/scripts/versions-replacer/src/main.rs @@ -0,0 +1,66 @@ +use std::path::PathBuf; + +use argh::FromArgs; +use color_eyre::{ + eyre::{eyre, Context}, + Result, +}; +use regex::Regex; +use walkdir::WalkDir; + +use versions_replacer::{ + metadata::collect_versions_from_cargo_toml, replace::replace_versions_in_file, +}; + +#[derive(FromArgs)] +/// Replace variables like '{{{{versions.fuels}}}}' with correct versions from Cargo.toml. +/// Uses versions from '[workspace.members]' and '[workspace.metadata.versions-replacer.external-versions]'. +struct VersionsReplacer { + /// path to directory with files containing variables + #[argh(positional)] + path: PathBuf, + /// path to Cargo.toml with versions + #[argh(option)] + manifest_path: PathBuf, + /// regex to filter filenames (example: "\.md$") + #[argh(option)] + filename_regex: Option, +} + +fn main() -> Result<()> { + let args: VersionsReplacer = argh::from_env(); + let versions = collect_versions_from_cargo_toml(&args.manifest_path)?; + + let mut total_replacements: Vec = Vec::new(); + + for entry in WalkDir::new(&args.path) { + let entry = entry.wrap_err("failed to get directory entry")?; + + if entry.path().is_file() { + if let Some(filename_regex) = &args.filename_regex { + let file_name = entry + .path() + .file_name() + .ok_or_else(|| eyre!("{:?} has an invalid file name", entry.path()))? + .to_str() + .ok_or_else(|| eyre!("filename is not valid UTF-8"))?; + if !filename_regex.is_match(file_name) { + continue; + } + } + + let replacement_count = replace_versions_in_file(entry.path(), &versions) + .wrap_err_with(|| format!("failed to replace versions in {:?}", entry.path()))?; + if replacement_count > 0 { + total_replacements.push(replacement_count); + } + } + } + + println!( + "replaced {} variables across {} files", + total_replacements.iter().sum::(), + total_replacements.len() + ); + Ok(()) +} diff --git a/scripts/versions-replacer/src/metadata.rs b/scripts/versions-replacer/src/metadata.rs new file mode 100644 index 0000000000..3858d6a4d2 --- /dev/null +++ b/scripts/versions-replacer/src/metadata.rs @@ -0,0 +1,32 @@ +use std::{collections::HashMap, path::Path}; + +use cargo_metadata::MetadataCommand; +use color_eyre::{eyre::Context, Result}; +use serde::Deserialize; + +#[derive(Deserialize)] +#[serde(rename_all = "kebab-case")] +pub struct WorkspaceMetadata { + pub versions_replacer: VersionsReplacerMetadata, +} + +#[derive(Deserialize)] +#[serde(rename_all = "kebab-case")] +pub struct VersionsReplacerMetadata { + pub external_versions: HashMap, +} + +pub fn collect_versions_from_cargo_toml( + manifest_path: impl AsRef, +) -> Result> { + let metadata = MetadataCommand::new() + .manifest_path(manifest_path.as_ref()) + .exec() + .wrap_err("failed to execute 'cargo metadata'")?; + let version_map = metadata + .packages + .iter() + .map(|package| (package.name.clone(), package.version.to_string())) + .collect::>(); + Ok(version_map) +} diff --git a/scripts/versions-replacer/src/replace.rs b/scripts/versions-replacer/src/replace.rs new file mode 100644 index 0000000000..7bc1893c1f --- /dev/null +++ b/scripts/versions-replacer/src/replace.rs @@ -0,0 +1,86 @@ +use std::{borrow::Cow, collections::HashMap, fs, path::Path}; + +use color_eyre::{eyre::Context, Result}; +use once_cell::sync::Lazy; +use regex::{Captures, Regex}; + +pub static VERSIONS_REGEX: Lazy = + Lazy::new(|| Regex::new(r"\{\{versions\.([\w_-]+)\}\}").unwrap()); + +pub fn replace_versions_in_file( + path: impl AsRef, + versions: &HashMap, +) -> Result { + let path = path.as_ref(); + let contents = + fs::read_to_string(path).wrap_err_with(|| format!("failed to read {:?}", path))?; + let (replaced_contents, replacement_count) = replace_versions_in_string(&contents, versions); + if replacement_count > 0 { + fs::write(path, replaced_contents.as_bytes()) + .wrap_err_with(|| format!("failed to write back to {:?}", path))?; + } + Ok(replacement_count) +} + +pub fn replace_versions_in_string<'a>( + s: &'a str, + versions: &HashMap, +) -> (Cow<'a, str>, usize) { + let mut replacement_count = 0; + let replaced_s = VERSIONS_REGEX.replace_all(s, |caps: &Captures| { + if let Some(version) = versions.get(&caps[1]) { + replacement_count += 1; + version.clone() + } else { + // leave unchanged + caps[0].to_string() + } + }); + (replaced_s, replacement_count) +} + +#[cfg(test)] +mod tests { + use super::*; + + fn test_versions() -> HashMap { + [("fuels", "0.47.0"), ("fuel-types", "0.35.3")] + .map(|(name, version)| (name.to_string(), version.to_string())) + .into() + } + + #[test] + fn test_valid_replacements() { + let s = "docs.rs/fuels/{{versions.fuels}}/fuels\ndocs.rs/fuel-types/{{versions.fuel-types}}/fuel-types"; + let versions = test_versions(); + let (replaced, count) = replace_versions_in_string(s, &versions); + assert_eq!( + replaced, + format!( + "docs.rs/fuels/{}/fuels\ndocs.rs/fuel-types/{}/fuel-types", + versions["fuels"], versions["fuel-types"] + ) + ); + assert_eq!(count, 2); + } + + #[test] + fn test_invalid_replacement() { + let s = "```rust,ignore +{{#include ../../../examples/contracts/src/lib.rs:deployed_contracts}} +```"; + let versions = test_versions(); + let (replaced, count) = replace_versions_in_string(s, &versions); + assert_eq!(replaced, s); + assert_eq!(count, 0); + } + + #[test] + fn test_invalid_package_name() { + let s = "docs.rs/fuels-wrong-name/{{versions.fuels-wrong-name}}/fuels-wrong-name"; + let versions = test_versions(); + let (replaced, count) = replace_versions_in_string(s, &versions); + assert_eq!(replaced, s); + assert_eq!(count, 0); + } +}